Merge branch 'android-updates'

Fixes the roaming behavior on Android 5+, a linker issue on Android M,
a few bugs, and adds several new advanced options for VPN profile (MTU,
server port, split tunneling).

Also adds methods and a constructor to parse settings_t from a string
instead of a file.

Fixes #782, #847, #865.
This commit is contained in:
Tobias Brunner
2015-07-28 13:55:23 +02:00
38 changed files with 1242 additions and 399 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (C) 2012-2014 Tobias Brunner Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil Hochschule fuer Technik Rapperswil
@@ -20,7 +20,7 @@
android:versionCode="26" android:versionCode="26"
android:versionName="1.4.5" > android:versionName="1.4.5" >
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+1 -1
View File
@@ -6,7 +6,7 @@ include $(CLEAR_VARS)
strongswan_USE_BYOD := true strongswan_USE_BYOD := true
strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \ strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \
pkcs1 pkcs8 pem xcbc hmac socket-default kernel-netlink \ pkcs1 pkcs8 pem xcbc hmac socket-default \
eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls
ifneq ($(strongswan_USE_BYOD),) ifneq ($(strongswan_USE_BYOD),)
+1
View File
@@ -1,2 +1,3 @@
# select the ABI(s) to build for (see CPU-ARCH-ABIS.html in the NDK docs). # select the ABI(s) to build for (see CPU-ARCH-ABIS.html in the NDK docs).
APP_ABI := armeabi x86 mips APP_ABI := armeabi x86 mips
APP_PLATFORM := android-19
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -15,6 +15,8 @@
* for more details. * for more details.
*/ */
#include <dlfcn.h>
#include "android_jni.h" #include "android_jni.h"
#include <library.h> #include <library.h>
@@ -25,6 +27,21 @@
*/ */
static JavaVM *android_jvm; static JavaVM *android_jvm;
static struct {
char name[32];
void *handle;
} libs[] = {
{ "libstrongswan.so", NULL },
#ifdef USE_BYOD
{ "libtncif.so", NULL },
{ "libtnccs.so", NULL },
{ "libimcv.so", NULL },
#endif
{ "libhydra.so", NULL },
{ "libcharon.so", NULL },
{ "libipsec.so", NULL },
};
jclass *android_charonvpnservice_class; jclass *android_charonvpnservice_class;
jclass *android_charonvpnservice_builder_class; jclass *android_charonvpnservice_builder_class;
android_sdk_version_t android_sdk_version; android_sdk_version_t android_sdk_version;
@@ -79,6 +96,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
JNIEnv *env; JNIEnv *env;
jclass jversion; jclass jversion;
jfieldID jsdk_int; jfieldID jsdk_int;
int i;
android_jvm = vm; android_jvm = vm;
@@ -87,6 +105,15 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
return -1; return -1;
} }
for (i = 0; i < countof(libs); i++)
{
libs[i].handle = dlopen(libs[i].name, RTLD_GLOBAL);
if (!libs[i].handle)
{
return -1;
}
}
androidjni_threadlocal = thread_value_create(attached_thread_cleanup); androidjni_threadlocal = thread_value_create(attached_thread_cleanup);
android_charonvpnservice_class = android_charonvpnservice_class =
@@ -109,6 +136,16 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
*/ */
void JNI_OnUnload(JavaVM *vm, void *reserved) void JNI_OnUnload(JavaVM *vm, void *reserved)
{ {
int i;
androidjni_threadlocal->destroy(androidjni_threadlocal); androidjni_threadlocal->destroy(androidjni_threadlocal);
for (i = countof(libs) - 1; i >= 0; i--)
{
if (libs[i].handle)
{
dlclose(libs[i].handle);
}
}
} }
@@ -54,6 +54,8 @@ typedef enum {
ANDROID_ICE_CREAM_SANDWICH = 14, ANDROID_ICE_CREAM_SANDWICH = 14,
ANDROID_ICE_CREAM_SANDWICH_MR1 = 15, ANDROID_ICE_CREAM_SANDWICH_MR1 = 15,
ANDROID_JELLY_BEAN = 16, ANDROID_JELLY_BEAN = 16,
ANDROID_JELLY_BEAN_MR1 = 17,
ANDROID_JELLY_BEAN_MR2 = 18,
} android_sdk_version_t; } android_sdk_version_t;
/** /**
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2010-2014 Tobias Brunner * Copyright (C) 2010-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -32,8 +32,6 @@
typedef struct private_android_service_t private_android_service_t; typedef struct private_android_service_t private_android_service_t;
#define TUN_DEFAULT_MTU 1400
/** /**
* private data of Android service * private data of Android service
*/ */
@@ -55,24 +53,9 @@ struct private_android_service_t {
ike_sa_t *ike_sa; ike_sa_t *ike_sa;
/** /**
* the type of VPN * configuration setttings
*/ */
char *type; settings_t *settings;
/**
* gateway
*/
char *gateway;
/**
* username
*/
char *username;
/**
* password
*/
char *password;
/** /**
* lock to safely access the TUN device fd * lock to safely access the TUN device fd
@@ -84,6 +67,11 @@ struct private_android_service_t {
*/ */
int tunfd; int tunfd;
/**
* MTU of TUN device
*/
int mtu;
/** /**
* DNS proxy * DNS proxy
*/ */
@@ -191,7 +179,7 @@ static job_requeue_t handle_plain(private_android_service_t *this)
return JOB_REQUEUE_DIRECT; return JOB_REQUEUE_DIRECT;
} }
raw = chunk_alloc(TUN_DEFAULT_MTU); raw = chunk_alloc(this->mtu);
len = read(tunfd, raw.ptr, raw.len); len = read(tunfd, raw.ptr, raw.len);
if (len < 0) if (len < 0)
{ {
@@ -309,7 +297,7 @@ static bool setup_tun_device(private_android_service_t *this,
return FALSE; return FALSE;
} }
if (!add_routes(builder, child_sa) || if (!add_routes(builder, child_sa) ||
!builder->set_mtu(builder, TUN_DEFAULT_MTU)) !builder->set_mtu(builder, this->mtu))
{ {
return FALSE; return FALSE;
} }
@@ -621,6 +609,7 @@ static void add_auth_cfg_pw(private_android_service_t *this,
{ {
identification_t *user; identification_t *user;
auth_cfg_t *auth; auth_cfg_t *auth;
char *username, *password;
auth = auth_cfg_create(); auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
@@ -629,12 +618,14 @@ static void add_auth_cfg_pw(private_android_service_t *this,
auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TTLS); auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TTLS);
} }
user = identification_create_from_string(this->username); username = this->settings->get_str(this->settings, "connection.username",
NULL);
password = this->settings->get_str(this->settings, "connection.password",
NULL);
user = identification_create_from_string(username);
auth->add(auth, AUTH_RULE_IDENTITY, user); auth->add(auth, AUTH_RULE_IDENTITY, user);
this->creds->add_username_password(this->creds, this->username, this->creds->add_username_password(this->creds, username, password);
this->password);
memwipe(this->password, strlen(this->password));
peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE); peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
} }
@@ -644,6 +635,7 @@ static bool add_auth_cfg_cert(private_android_service_t *this,
certificate_t *cert; certificate_t *cert;
identification_t *id; identification_t *id;
auth_cfg_t *auth; auth_cfg_t *auth;
char *type;
cert = this->creds->load_user_certificate(this->creds); cert = this->creds->load_user_certificate(this->creds);
if (!cert) if (!cert)
@@ -651,8 +643,9 @@ static bool add_auth_cfg_cert(private_android_service_t *this,
return FALSE; return FALSE;
} }
type = this->settings->get_str(this->settings, "connection.type", NULL);
auth = auth_cfg_create(); auth = auth_cfg_create();
if (strpfx("ikev2-eap-tls", this->type)) if (strpfx("ikev2-eap-tls", type))
{ {
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS); auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS);
@@ -687,11 +680,15 @@ static job_requeue_t initiate(private_android_service_t *this)
.jitter = 300 /* 5min */ .jitter = 300 /* 5min */
} }
}; };
char *type, *server;
int port;
server = this->settings->get_str(this->settings, "connection.server", NULL);
port = this->settings->get_int(this->settings, "connection.port",
IKEV2_UDP_PORT);
ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0", ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0",
charon->socket->get_port(charon->socket, FALSE), charon->socket->get_port(charon->socket, FALSE),
this->gateway, IKEV2_UDP_PORT, server, port, FRAGMENTATION_YES, 0);
FRAGMENTATION_YES, 0);
ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE)); ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
@@ -705,10 +702,11 @@ static job_requeue_t initiate(private_android_service_t *this)
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET)); peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET));
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6)); peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6));
type = this->settings->get_str(this->settings, "connection.type", NULL);
/* local auth config */ /* local auth config */
if (streq("ikev2-cert", this->type) || if (streq("ikev2-cert", type) ||
streq("ikev2-cert-eap", this->type) || streq("ikev2-cert-eap", type) ||
streq("ikev2-eap-tls", this->type)) streq("ikev2-eap-tls", type))
{ {
if (!add_auth_cfg_cert(this, peer_cfg)) if (!add_auth_cfg_cert(this, peer_cfg))
{ {
@@ -718,16 +716,16 @@ static job_requeue_t initiate(private_android_service_t *this)
return JOB_REQUEUE_NONE; return JOB_REQUEUE_NONE;
} }
} }
if (streq("ikev2-eap", this->type) || if (streq("ikev2-eap", type) ||
streq("ikev2-cert-eap", this->type) || streq("ikev2-cert-eap", type) ||
streq("ikev2-byod-eap", this->type)) streq("ikev2-byod-eap", type))
{ {
add_auth_cfg_pw(this, peer_cfg, strpfx(this->type, "ikev2-byod")); add_auth_cfg_pw(this, peer_cfg, strpfx(type, "ikev2-byod"));
} }
/* remote auth config */ /* remote auth config */
auth = auth_cfg_create(); auth = auth_cfg_create();
gateway = identification_create_from_string(this->gateway); gateway = identification_create_from_string(server);
auth->add(auth, AUTH_RULE_IDENTITY, gateway); auth->add(auth, AUTH_RULE_IDENTITY, gateway);
auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE); auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
@@ -806,23 +804,15 @@ METHOD(android_service_t, destroy, void,
close_tun_device(this); close_tun_device(this);
this->dns_proxy->destroy(this->dns_proxy); this->dns_proxy->destroy(this->dns_proxy);
this->lock->destroy(this->lock); this->lock->destroy(this->lock);
free(this->type); this->settings->destroy(this->settings);
free(this->gateway);
free(this->username);
if (this->password)
{
memwipe(this->password, strlen(this->password));
free(this->password);
}
free(this); free(this);
} }
/** /**
* See header * See header
*/ */
android_service_t *android_service_create(android_creds_t *creds, char *type, android_service_t *android_service_create(android_creds_t *creds,
char *gateway, char *username, settings_t *settings)
char *password)
{ {
private_android_service_t *this; private_android_service_t *this;
@@ -840,15 +830,14 @@ android_service_t *android_service_create(android_creds_t *creds, char *type,
}, },
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
.dns_proxy = android_dns_proxy_create(), .dns_proxy = android_dns_proxy_create(),
.username = username, .settings = settings,
.password = password,
.gateway = gateway,
.creds = creds, .creds = creds,
.type = type,
.tunfd = -1, .tunfd = -1,
.mtu = settings->get_int(settings, "global.mtu", ANDROID_DEFAULT_MTU),
); );
/* only allow queries for the VPN gateway */ /* only allow queries for the VPN gateway */
this->dns_proxy->add_hostname(this->dns_proxy, gateway); this->dns_proxy->add_hostname(this->dns_proxy,
this->settings->get_str(this->settings, "connection.server", NULL));
charon->bus->add_listener(charon->bus, &this->public.listener); charon->bus->add_listener(charon->bus, &this->public.listener);
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2010-2012 Tobias Brunner * Copyright (C) 2010-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -52,13 +52,9 @@ struct android_service_t {
* new IKE SA. * new IKE SA.
* *
* @param creds Android specific credential set * @param creds Android specific credential set
* @param type VPN type (see VpnType.java) * @param settings configuration settings (gets adopted)
* @param gateway gateway address
* @param username user name (local identity)
* @param password password (if any)
*/ */
android_service_t *android_service_create(android_creds_t *creds, char *type, android_service_t *android_service_create(android_creds_t *creds,
char *gateway, char *username, settings_t *settings);
char *password);
#endif /** ANDROID_SERVICE_H_ @}*/ #endif /** ANDROID_SERVICE_H_ @}*/
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -44,7 +44,6 @@
#define ANDROID_RETRASNMIT_TRIES 3 #define ANDROID_RETRASNMIT_TRIES 3
#define ANDROID_RETRANSMIT_TIMEOUT 2.0 #define ANDROID_RETRANSMIT_TIMEOUT 2.0
#define ANDROID_RETRANSMIT_BASE 1.4 #define ANDROID_RETRANSMIT_BASE 1.4
#define ANDROID_FRAGMENT_SIZE 1400
typedef struct private_charonservice_t private_charonservice_t; typedef struct private_charonservice_t private_charonservice_t;
@@ -83,11 +82,6 @@ struct private_charonservice_t {
*/ */
network_manager_t *network_manager; network_manager_t *network_manager;
/**
* Handle network events
*/
android_net_t *net_handler;
/** /**
* CharonVpnService reference * CharonVpnService reference
*/ */
@@ -400,18 +394,27 @@ METHOD(charonservice_t, get_network_manager, network_manager_t*,
/** /**
* Initiate a new connection * Initiate a new connection
* *
* @param gateway gateway address (gets owned) * @param settings configuration settings (gets owned)
* @param username username (gets owned)
* @param password password (gets owned)
*/ */
static void initiate(char *type, char *gateway, char *username, char *password) static void initiate(settings_t *settings)
{ {
private_charonservice_t *this = (private_charonservice_t*)charonservice; private_charonservice_t *this = (private_charonservice_t*)charonservice;
lib->settings->set_str(lib->settings,
"charon.plugins.tnc-imc.preferred_language",
settings->get_str(settings, "global.language", "en"));
/* this is actually the size of the complete IKE/IP packet, so if the MTU
* for the TUN devices has to be reduced to pass traffic the IKE packets
* will be a bit smaller than necessary as there is no IPsec overhead like
* for the tunneled traffic (but compensating that seems like overkill) */
lib->settings->set_int(lib->settings,
"charon.fragment_size",
settings->get_int(settings, "global.mtu",
ANDROID_DEFAULT_MTU));
this->creds->clear(this->creds); this->creds->clear(this->creds);
DESTROY_IF(this->service); DESTROY_IF(this->service);
this->service = android_service_create(this->creds, type, gateway, this->service = android_service_create(this->creds, settings);
username, password);
} }
/** /**
@@ -423,14 +426,12 @@ static bool charonservice_register(plugin_t *plugin, plugin_feature_t *feature,
private_charonservice_t *this = (private_charonservice_t*)charonservice; private_charonservice_t *this = (private_charonservice_t*)charonservice;
if (reg) if (reg)
{ {
this->net_handler = android_net_create();
lib->credmgr->add_set(lib->credmgr, &this->creds->set); lib->credmgr->add_set(lib->credmgr, &this->creds->set);
charon->attributes->add_handler(charon->attributes, charon->attributes->add_handler(charon->attributes,
&this->attr->handler); &this->attr->handler);
} }
else else
{ {
this->net_handler->destroy(this->net_handler);
lib->credmgr->remove_set(lib->credmgr, &this->creds->set); lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
charon->attributes->remove_handler(charon->attributes, charon->attributes->remove_handler(charon->attributes,
&this->attr->handler); &this->attr->handler);
@@ -466,8 +467,8 @@ static void set_options(char *logfile)
"charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT); "charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT);
lib->settings->set_double(lib->settings, lib->settings->set_double(lib->settings,
"charon.retransmit_base", ANDROID_RETRANSMIT_BASE); "charon.retransmit_base", ANDROID_RETRANSMIT_BASE);
lib->settings->set_int(lib->settings, lib->settings->set_bool(lib->settings,
"charon.fragment_size", ANDROID_FRAGMENT_SIZE); "charon.initiator_only", TRUE);
lib->settings->set_bool(lib->settings, lib->settings->set_bool(lib->settings,
"charon.close_ike_on_child_failure", TRUE); "charon.close_ike_on_child_failure", TRUE);
/* setting the source address breaks the VpnService.protect() function which /* setting the source address breaks the VpnService.protect() function which
@@ -483,19 +484,6 @@ static void set_options(char *logfile)
* so lets disable IPv6 for now to avoid issues with dual-stack gateways */ * so lets disable IPv6 for now to avoid issues with dual-stack gateways */
lib->settings->set_bool(lib->settings, lib->settings->set_bool(lib->settings,
"charon.plugins.socket-default.use_ipv6", FALSE); "charon.plugins.socket-default.use_ipv6", FALSE);
/* don't install virtual IPs via kernel-netlink */
lib->settings->set_bool(lib->settings,
"charon.install_virtual_ip", FALSE);
/* kernel-netlink should not trigger roam events, we use Android's
* ConnectivityManager for that, much less noise */
lib->settings->set_bool(lib->settings,
"charon.plugins.kernel-netlink.roam_events", FALSE);
/* ignore tun devices (it's mostly tun0 but it may already be taken, ignore
* some others too), also ignore lo as a default route points to it when
* no connectivity is available */
lib->settings->set_str(lib->settings,
"charon.interfaces_ignore", "lo, tun0, tun1, tun2, tun3, "
"tun4");
#ifdef USE_BYOD #ifdef USE_BYOD
lib->settings->set_str(lib->settings, lib->settings->set_str(lib->settings,
@@ -519,6 +507,8 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder,
static plugin_feature_t features[] = { static plugin_feature_t features[] = {
PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create), PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create),
PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
PLUGIN_CALLBACK(charonservice_register, NULL), PLUGIN_CALLBACK(charonservice_register, NULL),
PLUGIN_PROVIDE(CUSTOM, "android-backend"), PLUGIN_PROVIDE(CUSTOM, "android-backend"),
PLUGIN_DEPENDS(CUSTOM, "libcharon"), PLUGIN_DEPENDS(CUSTOM, "libcharon"),
@@ -705,14 +695,12 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void)
* Initiate SA * Initiate SA
*/ */
JNI_METHOD(CharonVpnService, initiate, void, JNI_METHOD(CharonVpnService, initiate, void,
jstring jtype, jstring jgateway, jstring jusername, jstring jpassword) jstring jconfig)
{ {
char *type, *gateway, *username, *password; settings_t *settings;
char *config;
type = androidjni_convert_jstring(env, jtype); config = androidjni_convert_jstring(env, jconfig);
gateway = androidjni_convert_jstring(env, jgateway); settings = settings_create_string(config);
username = androidjni_convert_jstring(env, jusername); initiate(settings);
password = androidjni_convert_jstring(env, jpassword);
initiate(type, gateway, username, password);
} }
@@ -44,6 +44,11 @@ typedef enum android_vpn_state_t android_vpn_state_t;
typedef enum android_imc_state_t android_imc_state_t; typedef enum android_imc_state_t android_imc_state_t;
typedef struct charonservice_t charonservice_t; typedef struct charonservice_t charonservice_t;
/**
* Default value for the MTU of TUN device and the size of IKE fragments
*/
#define ANDROID_DEFAULT_MTU 1400
/** /**
* VPN status codes. As defined in CharonVpnService.java * VPN status codes. As defined in CharonVpnService.java
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -11,9 +11,14 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. * for more details.
*/ */
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include "android_net.h" #include "android_net.h"
#include "../android_jni.h"
#include "../charonservice.h" #include "../charonservice.h"
#include <hydra.h> #include <hydra.h>
#include <processing/jobs/callback_job.h> #include <processing/jobs/callback_job.h>
@@ -21,6 +26,7 @@
/** delay before firing roam events (ms) */ /** delay before firing roam events (ms) */
#define ROAM_DELAY 100 #define ROAM_DELAY 100
#define ROAM_DELAY_RECHECK 1000
typedef struct private_android_net_t private_android_net_t; typedef struct private_android_net_t private_android_net_t;
@@ -29,7 +35,7 @@ struct private_android_net_t {
/** /**
* Public kernel interface * Public kernel interface
*/ */
android_net_t public; kernel_net_t public;
/** /**
* Reference to NetworkManager object * Reference to NetworkManager object
@@ -37,14 +43,29 @@ struct private_android_net_t {
network_manager_t *network_manager; network_manager_t *network_manager;
/** /**
* earliest time of the next roam event * Earliest time of the next roam event
*/ */
timeval_t next_roam; timeval_t next_roam;
/** /**
* mutex to check and update roam event time * Mutex to check and update roam event time, and other private members
*/ */
mutex_t *mutex; mutex_t *mutex;
/**
* List of virtual IPs
*/
linked_list_t *vips;
/**
* Socket used to determine source address
*/
int socket_v4;
/**
* Whether the device is currently connected
*/
bool connected;
}; };
/** /**
@@ -69,6 +90,7 @@ static void connectivity_cb(private_android_net_t *this,
time_monotonic(&now); time_monotonic(&now);
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
this->connected = !disconnected;
if (!timercmp(&now, &this->next_roam, >)) if (!timercmp(&now, &this->next_roam, >))
{ {
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
@@ -83,32 +105,210 @@ static void connectivity_cb(private_android_net_t *this,
lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
} }
METHOD(android_net_t, destroy, void, METHOD(kernel_net_t, get_source_addr, host_t*,
private_android_net_t *this, host_t *dest, host_t *src)
{
union {
struct sockaddr sockaddr;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} addr;
socklen_t addrlen;
timeval_t now;
job_t *job;
addrlen = *dest->get_sockaddr_len(dest);
addr.sockaddr.sa_family = AF_UNSPEC;
if (connect(this->socket_v4, &addr.sockaddr, addrlen) < 0)
{
DBG1(DBG_KNL, "failed to disconnect socket: %s", strerror(errno));
return NULL;
}
if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
{ /* this seems to help avoiding the VIP, unless there is no connectivity
* at all */
charonservice->bypass_socket(charonservice, -1, 0);
}
if (connect(this->socket_v4, dest->get_sockaddr(dest), addrlen) < 0)
{
/* don't report an error if we are not connected (ENETUNREACH) */
if (errno != ENETUNREACH)
{
DBG1(DBG_KNL, "failed to connect socket: %s", strerror(errno));
}
else
{
time_monotonic(&now);
this->mutex->lock(this->mutex);
if (this->connected && timercmp(&now, &this->next_roam, >))
{ /* we were not able to find a source address but reportedly are
* connected, trigger a recheck in case an IP address appears
* delayed but the callback is not triggered again */
timeval_add_ms(&now, ROAM_DELAY_RECHECK);
this->next_roam = now;
this->mutex->unlock(this->mutex);
job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
NULL, NULL, NULL);
lib->scheduler->schedule_job_ms(lib->scheduler, job,
ROAM_DELAY_RECHECK);
}
else
{
this->mutex->unlock(this->mutex);
}
}
return NULL;
}
if (getsockname(this->socket_v4, &addr.sockaddr, &addrlen) < 0)
{
DBG1(DBG_KNL, "failed to determine src address: %s", strerror(errno));
return NULL;
}
return host_create_from_sockaddr((sockaddr_t*)&addr);
}
METHOD(kernel_net_t, get_source_addr_old, host_t*,
private_android_net_t *this, host_t *dest, host_t *src)
{
host_t *host;
/* on older Android versions we might get the virtual IP back because
* the protect() implementation there and connect() don't properly work
* together, on newer releases (using fwmarks) that's not a problem */
host = get_source_addr(this, dest, src);
if (host)
{
this->mutex->lock(this->mutex);
if (this->vips->find_first(this->vips, (void*)host->ip_equals,
NULL, host) == SUCCESS)
{
host->destroy(host);
host = NULL;
}
this->mutex->unlock(this->mutex);
}
return host;
}
METHOD(kernel_net_t, get_nexthop, host_t*,
private_android_net_t *this, host_t *dest, int prefix, host_t *src)
{
return NULL;
}
METHOD(kernel_net_t, get_interface, bool,
private_android_net_t *this, host_t *host, char **name)
{
if (name)
{ /* the actual name does not matter in our case */
*name = strdup("strongswan");
}
return TRUE;
}
METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
private_android_net_t *this, kernel_address_type_t which)
{
/* return virtual IPs if requested, nothing otherwise */
if (which & ADDR_TYPE_VIRTUAL)
{
this->mutex->lock(this->mutex);
return enumerator_create_cleaner(
this->vips->create_enumerator(this->vips),
(void*)this->mutex->unlock, this->mutex);
}
return enumerator_create_empty();
}
METHOD(kernel_net_t, add_ip, status_t,
private_android_net_t *this, host_t *virtual_ip, int prefix, char *iface)
{
this->mutex->lock(this->mutex);
this->vips->insert_last(this->vips, virtual_ip->clone(virtual_ip));
this->mutex->unlock(this->mutex);
return SUCCESS;
}
METHOD(kernel_net_t, del_ip, status_t,
private_android_net_t *this, host_t *virtual_ip, int prefix, bool wait)
{
host_t *vip;
this->mutex->lock(this->mutex);
if (this->vips->find_first(this->vips, (void*)virtual_ip->ip_equals,
(void**)&vip, virtual_ip) == SUCCESS)
{
this->vips->remove(this->vips, vip, NULL);
vip->destroy(vip);
}
this->mutex->unlock(this->mutex);
return SUCCESS;
}
METHOD(kernel_net_t, add_route, status_t,
private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
{
return NOT_SUPPORTED;
}
METHOD(kernel_net_t, del_route, status_t,
private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
{
return NOT_SUPPORTED;
}
METHOD(kernel_net_t, destroy, void,
private_android_net_t *this) private_android_net_t *this)
{ {
this->network_manager->remove_connectivity_cb(this->network_manager, this->network_manager->remove_connectivity_cb(this->network_manager,
(void*)connectivity_cb); (void*)connectivity_cb);
this->mutex->destroy(this->mutex); this->mutex->destroy(this->mutex);
this->vips->destroy(this->vips);
close(this->socket_v4);
free(this); free(this);
} }
/* kernel_net_t *kernel_android_net_create()
* Described in header.
*/
android_net_t *android_net_create()
{ {
private_android_net_t *this; private_android_net_t *this;
INIT(this, INIT(this,
.public = { .public = {
.get_source_addr = _get_source_addr,
.get_nexthop = _get_nexthop,
.get_interface = _get_interface,
.create_address_enumerator = _create_address_enumerator,
.add_ip = _add_ip,
.del_ip = _del_ip,
.add_route = _add_route,
.del_route = _del_route,
.destroy = _destroy, .destroy = _destroy,
}, },
.mutex = mutex_create(MUTEX_TYPE_DEFAULT), .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.vips = linked_list_create(),
.network_manager = charonservice->get_network_manager(charonservice), .network_manager = charonservice->get_network_manager(charonservice),
); );
timerclear(&this->next_roam); timerclear(&this->next_roam);
this->network_manager->add_connectivity_cb(this->network_manager, if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
(void*)connectivity_cb, this); {
this->public.get_source_addr = _get_source_addr_old;
}
this->socket_v4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (this->socket_v4 < 0)
{
DBG1(DBG_KNL, "failed to create socket to lookup src addresses: %s",
strerror(errno));
}
charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET);
this->mutex->lock(this->mutex);
this->network_manager->add_connectivity_cb(
this->network_manager, (void*)connectivity_cb, this);
this->connected = this->network_manager->is_connected(this->network_manager);
this->mutex->unlock(this->mutex);
return &this->public; return &this->public;
}; }
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -22,25 +22,14 @@
#define ANDROID_NET_H_ #define ANDROID_NET_H_
#include <library.h> #include <library.h>
#include <kernel/kernel_net.h>
typedef struct android_net_t android_net_t;
/** /**
* Handle connectivity events from NetworkManager * Create an Android-specific kernel_net_t instance.
*/
struct android_net_t {
/**
* Destroy an android_net_t instance.
*/
void (*destroy)(android_net_t *this);
};
/**
* Create an android_net_t instance.
* *
* @return android_net_t instance * @return kernel_net_t instance
*/ */
android_net_t *android_net_create(); kernel_net_t *kernel_android_net_create();
#endif /** ANDROID_NET_H_ @}*/ #endif /** ANDROID_NET_H_ @}*/
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -90,8 +90,8 @@ METHOD(network_manager_t, add_connectivity_cb, void,
this->connectivity_cb.cb = cb; this->connectivity_cb.cb = cb;
this->connectivity_cb.data = data; this->connectivity_cb.data = data;
} }
androidjni_detach_thread();
} }
androidjni_detach_thread();
} }
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
} }
@@ -132,6 +132,28 @@ METHOD(network_manager_t, remove_connectivity_cb, void,
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
} }
METHOD(network_manager_t, is_connected, bool,
private_network_manager_t *this)
{
JNIEnv *env;
jmethodID method_id;
bool connected = FALSE;
androidjni_attach_thread(&env);
method_id = (*env)->GetMethodID(env, this->cls, "isConnected", "()Z");
if (!method_id)
{
androidjni_exception_occurred(env);
}
else
{
connected = (*env)->CallBooleanMethod(env, this->obj, method_id);
connected = !androidjni_exception_occurred(env) && connected;
}
androidjni_detach_thread();
return connected;
}
METHOD(network_manager_t, destroy, void, METHOD(network_manager_t, destroy, void,
private_network_manager_t *this) private_network_manager_t *this)
{ {
@@ -174,6 +196,7 @@ network_manager_t *network_manager_create(jobject context)
.public = { .public = {
.add_connectivity_cb = _add_connectivity_cb, .add_connectivity_cb = _add_connectivity_cb,
.remove_connectivity_cb = _remove_connectivity_cb, .remove_connectivity_cb = _remove_connectivity_cb,
.is_connected = _is_connected,
.destroy = _destroy, .destroy = _destroy,
}, },
.mutex = mutex_create(MUTEX_TYPE_DEFAULT), .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -65,6 +65,13 @@ struct network_manager_t {
void (*remove_connectivity_cb)(network_manager_t *this, void (*remove_connectivity_cb)(network_manager_t *this,
connectivity_cb_t cb); connectivity_cb_t cb);
/**
* Check whether we currently have connectivity
*
* @return TRUE if currently connected
*/
bool (*is_connected)(network_manager_t *this);
/** /**
* Destroy a network_manager_t instance * Destroy a network_manager_t instance
*/ */
+1 -1
View File
@@ -8,4 +8,4 @@
# project structure. # project structure.
# Project target. # Project target.
target=android-19 target=android-21
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (C) 2012 Tobias Brunner Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil Hochschule fuer Technik Rapperswil
@@ -139,6 +139,65 @@
android:id="@+id/select_certificate" android:id="@+id/select_certificate"
layout="@layout/two_line_button" /> layout="@layout/two_line_button" />
<CheckBox
android:id="@+id/show_advanced"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/profile_show_advanced_label" />
<LinearLayout
android:id="@+id/advanced_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/profile_mtu_label" />
<EditText
android:id="@+id/mtu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="number|textNoSuggestions"
android:hint="@string/profile_use_default_hint" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/profile_port_label" />
<EditText
android:id="@+id/port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="number|textNoSuggestions"
android:hint="@string/profile_use_default_hint" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/profile_split_tunneling_label" />
<CheckBox
android:id="@+id/split_tunneling_v4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/profile_split_tunnelingv4_title" />
<CheckBox
android:id="@+id/split_tunneling_v6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/profile_split_tunnelingv6_title" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (C) 2012-2013 Tobias Brunner Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil Hochschule fuer Technik Rapperswil
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Automatisch wählen</string> <string name="profile_ca_auto_label">Automatisch wählen</string>
<string name="profile_ca_select_certificate_label">CA-Zertifikat auswählen</string> <string name="profile_ca_select_certificate_label">CA-Zertifikat auswählen</string>
<string name="profile_ca_select_certificate">Wählen Sie ein bestimmtes CA-Zertifikat</string> <string name="profile_ca_select_certificate">Wählen Sie ein bestimmtes CA-Zertifikat</string>
<string name="profile_show_advanced_label">Erweiterte Einstellungen anzeigen</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server Port:</string>
<string name="profile_use_default_hint">(Standardwert verwenden)</string>
<string name="profile_split_tunneling_label">Split-Tunneling:</string>
<string name="profile_split_tunnelingv4_title">Blockiere IPv4 Verkehr der nicht für das VPN bestimmt ist</string>
<string name="profile_split_tunnelingv6_title">Blockiere IPv6 Verkehr der nicht für das VPN bestimmt ist</string>
<!-- Warnings/Notifications in the details view --> <!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Bitte geben Sie hier die Gateway-Adresse ein</string> <string name="alert_text_no_input_gateway">Bitte geben Sie hier die Gateway-Adresse ein</string>
<string name="alert_text_no_input_username">Bitte geben Sie hier Ihren Benutzernamen ein</string> <string name="alert_text_no_input_username">Bitte geben Sie hier Ihren Benutzernamen ein</string>
<string name="alert_text_nocertfound_title">Kein CA-Zertifikat ausgewählt</string> <string name="alert_text_nocertfound_title">Kein CA-Zertifikat ausgewählt</string>
<string name="alert_text_nocertfound">Bitte wählen Sie eines aus oder aktivieren Sie <i>Automatisch wählen</i></string> <string name="alert_text_nocertfound">Bitte wählen Sie eines aus oder aktivieren Sie <i>Automatisch wählen</i></string>
<string name="alert_text_out_of_range">Bitte geben Sie eine Nummer von %1$d - %2$d ein</string>
<string name="tnc_notice_title">EAP-TNC kann Ihre Privatsphäre beeinträchtigen</string> <string name="tnc_notice_title">EAP-TNC kann Ihre Privatsphäre beeinträchtigen</string>
<string name="tnc_notice_subtitle">Gerätedaten werden an den Gateway-Betreiber gesendet</string> <string name="tnc_notice_subtitle">Gerätedaten werden an den Gateway-Betreiber gesendet</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) erlaubt Gateway-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.&lt;/p>&lt;p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.&lt;/p>&lt;b>Solche Daten werden nur übermittelt nachdem die Identität des Gateways geprüft wurde.&lt;/b></string> <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) erlaubt Gateway-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.&lt;/p>&lt;p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.&lt;/p>&lt;b>Solche Daten werden nur übermittelt nachdem die Identität des Gateways geprüft wurde.&lt;/b></string>
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Wybierz automatycznie</string> <string name="profile_ca_auto_label">Wybierz automatycznie</string>
<string name="profile_ca_select_certificate_label">Wybierz certyfikat CA</string> <string name="profile_ca_select_certificate_label">Wybierz certyfikat CA</string>
<string name="profile_ca_select_certificate">Wybierz określony certyfikat CA</string> <string name="profile_ca_select_certificate">Wybierz określony certyfikat CA</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view --> <!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Wprowadź adres bramki</string> <string name="alert_text_no_input_gateway">Wprowadź adres bramki</string>
<string name="alert_text_no_input_username">Wprowadź swoją nazwę użytkownika</string> <string name="alert_text_no_input_username">Wprowadź swoją nazwę użytkownika</string>
<string name="alert_text_nocertfound_title">Nie wybrano żadnego certyfikatu CA</string> <string name="alert_text_nocertfound_title">Nie wybrano żadnego certyfikatu CA</string>
<string name="alert_text_nocertfound">Wybierz lub uaktywnij jeden <i>Wybierz automatycznie</i></string> <string name="alert_text_nocertfound">Wybierz lub uaktywnij jeden <i>Wybierz automatycznie</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string> <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string> <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string> <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
@@ -60,11 +60,19 @@
<string name="profile_ca_auto_label">Выбрать автоматически</string> <string name="profile_ca_auto_label">Выбрать автоматически</string>
<string name="profile_ca_select_certificate_label">Выбрать сертификат CA</string> <string name="profile_ca_select_certificate_label">Выбрать сертификат CA</string>
<string name="profile_ca_select_certificate">Выбрать CA сертификат</string> <string name="profile_ca_select_certificate">Выбрать CA сертификат</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view --> <!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Пожалуйста введите адрес шлюза</string> <string name="alert_text_no_input_gateway">Пожалуйста введите адрес шлюза</string>
<string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string> <string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string>
<string name="alert_text_nocertfound_title">Не выбран сертификат CA</string> <string name="alert_text_nocertfound_title">Не выбран сертификат CA</string>
<string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string> <string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string> <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string> <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string> <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
@@ -61,11 +61,19 @@
<string name="profile_ca_auto_label">Вибрати автоматично</string> <string name="profile_ca_auto_label">Вибрати автоматично</string>
<string name="profile_ca_select_certificate_label">Вибрати сертифікат CA</string> <string name="profile_ca_select_certificate_label">Вибрати сертифікат CA</string>
<string name="profile_ca_select_certificate">Вибрати спеціальний сертифікат CA</string> <string name="profile_ca_select_certificate">Вибрати спеціальний сертифікат CA</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view --> <!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Введіть адресу шлюза тут</string> <string name="alert_text_no_input_gateway">Введіть адресу шлюза тут</string>
<string name="alert_text_no_input_username">Введіть ім\'я користувача тут</string> <string name="alert_text_no_input_username">Введіть ім\'я користувача тут</string>
<string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string> <string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string>
<string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string> <string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string> <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string> <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string> <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
+9 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (C) 2012-2013 Tobias Brunner Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil Hochschule fuer Technik Rapperswil
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Select automatically</string> <string name="profile_ca_auto_label">Select automatically</string>
<string name="profile_ca_select_certificate_label">Select CA certificate</string> <string name="profile_ca_select_certificate_label">Select CA certificate</string>
<string name="profile_ca_select_certificate">Select a specific CA certificate</string> <string name="profile_ca_select_certificate">Select a specific CA certificate</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view --> <!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Please enter the gateway address here</string> <string name="alert_text_no_input_gateway">Please enter the gateway address here</string>
<string name="alert_text_no_input_username">Please enter your username here</string> <string name="alert_text_no_input_username">Please enter your username here</string>
<string name="alert_text_nocertfound_title">No CA certificate selected</string> <string name="alert_text_nocertfound_title">No CA certificate selected</string>
<string name="alert_text_nocertfound">Please select one or activate <i>Select automatically</i></string> <string name="alert_text_nocertfound">Please select one or activate <i>Select automatically</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string> <string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string> <string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string> <string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -17,9 +17,15 @@
package org.strongswan.android.data; package org.strongswan.android.data;
public class VpnProfile implements Cloneable public class VpnProfile implements Cloneable
{ {
/* While storing this as EnumSet would be nicer this simplifies storing it in a database */
public static final int SPLIT_TUNNELING_BLOCK_IPV4 = 1;
public static final int SPLIT_TUNNELING_BLOCK_IPV6 = 2;
private String mName, mGateway, mUsername, mPassword, mCertificate, mUserCertificate; private String mName, mGateway, mUsername, mPassword, mCertificate, mUserCertificate;
private Integer mMTU, mPort, mSplitTunneling;
private VpnType mVpnType; private VpnType mVpnType;
private long mId = -1; private long mId = -1;
@@ -103,6 +109,36 @@ public class VpnProfile implements Cloneable
this.mUserCertificate = alias; this.mUserCertificate = alias;
} }
public Integer getMTU()
{
return mMTU;
}
public void setMTU(Integer mtu)
{
this.mMTU = mtu;
}
public Integer getPort()
{
return mPort;
}
public void setPort(Integer port)
{
this.mPort = port;
}
public Integer getSplitTunneling()
{
return mSplitTunneling;
}
public void setSplitTunneling(Integer splitTunneling)
{
this.mSplitTunneling = splitTunneling;
}
@Override @Override
public String toString() public String toString()
{ {
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -40,6 +40,9 @@ public class VpnProfileDataSource
public static final String KEY_PASSWORD = "password"; public static final String KEY_PASSWORD = "password";
public static final String KEY_CERTIFICATE = "certificate"; public static final String KEY_CERTIFICATE = "certificate";
public static final String KEY_USER_CERTIFICATE = "user_certificate"; public static final String KEY_USER_CERTIFICATE = "user_certificate";
public static final String KEY_MTU = "mtu";
public static final String KEY_PORT = "port";
public static final String KEY_SPLIT_TUNNELING = "split_tunneling";
private DatabaseHelper mDbHelper; private DatabaseHelper mDbHelper;
private SQLiteDatabase mDatabase; private SQLiteDatabase mDatabase;
@@ -48,7 +51,7 @@ public class VpnProfileDataSource
private static final String DATABASE_NAME = "strongswan.db"; private static final String DATABASE_NAME = "strongswan.db";
private static final String TABLE_VPNPROFILE = "vpnprofile"; private static final String TABLE_VPNPROFILE = "vpnprofile";
private static final int DATABASE_VERSION = 4; private static final int DATABASE_VERSION = 7;
public static final String DATABASE_CREATE = public static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_VPNPROFILE + " (" + "CREATE TABLE " + TABLE_VPNPROFILE + " (" +
@@ -59,7 +62,10 @@ public class VpnProfileDataSource
KEY_USERNAME + " TEXT," + KEY_USERNAME + " TEXT," +
KEY_PASSWORD + " TEXT," + KEY_PASSWORD + " TEXT," +
KEY_CERTIFICATE + " TEXT," + KEY_CERTIFICATE + " TEXT," +
KEY_USER_CERTIFICATE + " TEXT" + KEY_USER_CERTIFICATE + " TEXT," +
KEY_MTU + " INTEGER," +
KEY_PORT + " INTEGER," +
KEY_SPLIT_TUNNELING + " INTEGER" +
");"; ");";
private static final String[] ALL_COLUMNS = new String[] { private static final String[] ALL_COLUMNS = new String[] {
KEY_ID, KEY_ID,
@@ -70,6 +76,9 @@ public class VpnProfileDataSource
KEY_PASSWORD, KEY_PASSWORD,
KEY_CERTIFICATE, KEY_CERTIFICATE,
KEY_USER_CERTIFICATE, KEY_USER_CERTIFICATE,
KEY_MTU,
KEY_PORT,
KEY_SPLIT_TUNNELING,
}; };
private static class DatabaseHelper extends SQLiteOpenHelper private static class DatabaseHelper extends SQLiteOpenHelper
@@ -104,6 +113,21 @@ public class VpnProfileDataSource
{ /* remove NOT NULL constraint from username column */ { /* remove NOT NULL constraint from username column */
updateColumns(db); updateColumns(db);
} }
if (oldVersion < 5)
{
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU +
" INTEGER;");
}
if (oldVersion < 6)
{
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT +
" INTEGER;");
}
if (oldVersion < 7)
{
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SPLIT_TUNNELING +
" INTEGER;");
}
} }
private void updateColumns(SQLiteDatabase db) private void updateColumns(SQLiteDatabase db)
@@ -255,6 +279,9 @@ public class VpnProfileDataSource
profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD))); profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD)));
profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE))); profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE)));
profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_USER_CERTIFICATE))); profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_USER_CERTIFICATE)));
profile.setMTU(getInt(cursor, cursor.getColumnIndex(KEY_MTU)));
profile.setPort(getInt(cursor, cursor.getColumnIndex(KEY_PORT)));
profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndex(KEY_SPLIT_TUNNELING)));
return profile; return profile;
} }
@@ -268,6 +295,14 @@ public class VpnProfileDataSource
values.put(KEY_PASSWORD, profile.getPassword()); values.put(KEY_PASSWORD, profile.getPassword());
values.put(KEY_CERTIFICATE, profile.getCertificateAlias()); values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias()); values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
values.put(KEY_MTU, profile.getMTU());
values.put(KEY_PORT, profile.getPort());
values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
return values; return values;
} }
private Integer getInt(Cursor cursor, int columnIndex)
{
return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
}
} }
@@ -24,7 +24,7 @@ public enum VpnType
IKEV2_CERT("ikev2-cert", EnumSet.of(VpnTypeFeature.CERTIFICATE)), IKEV2_CERT("ikev2-cert", EnumSet.of(VpnTypeFeature.CERTIFICATE)),
IKEV2_CERT_EAP("ikev2-cert-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE)), IKEV2_CERT_EAP("ikev2-cert-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE)),
IKEV2_EAP_TLS("ikev2-eap-tls", EnumSet.of(VpnTypeFeature.CERTIFICATE)), IKEV2_EAP_TLS("ikev2-eap-tls", EnumSet.of(VpnTypeFeature.CERTIFICATE)),
IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE, VpnTypeFeature.BYOD)); IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.BYOD));
/** /**
* Features of a VPN type. * Features of a VPN type.
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager * Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
@@ -18,11 +18,16 @@
package org.strongswan.android.logic; package org.strongswan.android.logic;
import java.io.File; import java.io.File;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.strongswan.android.data.VpnProfile; import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource; import org.strongswan.android.data.VpnProfileDataSource;
@@ -32,7 +37,9 @@ import org.strongswan.android.logic.VpnStateService.State;
import org.strongswan.android.logic.imc.ImcState; import org.strongswan.android.logic.imc.ImcState;
import org.strongswan.android.logic.imc.RemediationInstruction; import org.strongswan.android.logic.imc.RemediationInstruction;
import org.strongswan.android.ui.MainActivity; import org.strongswan.android.ui.MainActivity;
import org.strongswan.android.utils.SettingsWriter;
import android.annotation.TargetApi;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
@@ -40,11 +47,13 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.net.VpnService; import android.net.VpnService;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.security.KeyChain; import android.security.KeyChain;
import android.security.KeyChainException; import android.security.KeyChainException;
import android.system.OsConstants;
import android.util.Log; import android.util.Log;
public class CharonVpnService extends VpnService implements Runnable public class CharonVpnService extends VpnService implements Runnable
@@ -211,13 +220,19 @@ public class CharonVpnService extends VpnService implements Runnable
startConnection(mCurrentProfile); startConnection(mCurrentProfile);
mIsDisconnecting = false; mIsDisconnecting = false;
BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName()); BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName(), mCurrentProfile.getSplitTunneling());
if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD))) if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD)))
{ {
Log.i(TAG, "charon started"); Log.i(TAG, "charon started");
initiate(mCurrentProfile.getVpnType().getIdentifier(), SettingsWriter writer = new SettingsWriter();
mCurrentProfile.getGateway(), mCurrentProfile.getUsername(), writer.setValue("global.language", Locale.getDefault().getLanguage());
mCurrentProfile.getPassword()); writer.setValue("global.mtu", mCurrentProfile.getMTU());
writer.setValue("connection.type", mCurrentProfile.getVpnType().getIdentifier());
writer.setValue("connection.server", mCurrentProfile.getGateway());
writer.setValue("connection.port", mCurrentProfile.getPort());
writer.setValue("connection.username", mCurrentProfile.getUsername());
writer.setValue("connection.password", mCurrentProfile.getPassword());
initiate(writer.serialize());
} }
else else
{ {
@@ -497,7 +512,6 @@ public class CharonVpnService extends VpnService implements Runnable
private PrivateKey getUserKey() throws KeyChainException, InterruptedException private PrivateKey getUserKey() throws KeyChainException, InterruptedException
{ {
return KeyChain.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias); return KeyChain.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias);
} }
/** /**
@@ -518,7 +532,7 @@ public class CharonVpnService extends VpnService implements Runnable
/** /**
* Initiate VPN, provided by libandroidbridge.so * Initiate VPN, provided by libandroidbridge.so
*/ */
public native void initiate(String type, String gateway, String username, String password); public native void initiate(String config);
/** /**
* Adapter for VpnService.Builder which is used to access it safely via JNI. * Adapter for VpnService.Builder which is used to access it safely via JNI.
@@ -527,15 +541,17 @@ public class CharonVpnService extends VpnService implements Runnable
public class BuilderAdapter public class BuilderAdapter
{ {
private final String mName; private final String mName;
private final Integer mSplitTunneling;
private VpnService.Builder mBuilder; private VpnService.Builder mBuilder;
private BuilderCache mCache; private BuilderCache mCache;
private BuilderCache mEstablishedCache; private BuilderCache mEstablishedCache;
public BuilderAdapter(String name) public BuilderAdapter(String name, Integer splitTunneling)
{ {
mName = name; mName = name;
mSplitTunneling = splitTunneling;
mBuilder = createBuilder(name); mBuilder = createBuilder(name);
mCache = new BuilderCache(); mCache = new BuilderCache(mSplitTunneling);
} }
private VpnService.Builder createBuilder(String name) private VpnService.Builder createBuilder(String name)
@@ -557,7 +573,6 @@ public class CharonVpnService extends VpnService implements Runnable
{ {
try try
{ {
mBuilder.addAddress(address, prefixLength);
mCache.addAddress(address, prefixLength); mCache.addAddress(address, prefixLength);
} }
catch (IllegalArgumentException ex) catch (IllegalArgumentException ex)
@@ -572,6 +587,7 @@ public class CharonVpnService extends VpnService implements Runnable
try try
{ {
mBuilder.addDnsServer(address); mBuilder.addDnsServer(address);
mCache.recordAddressFamily(address);
} }
catch (IllegalArgumentException ex) catch (IllegalArgumentException ex)
{ {
@@ -584,7 +600,6 @@ public class CharonVpnService extends VpnService implements Runnable
{ {
try try
{ {
mBuilder.addRoute(address, prefixLength);
mCache.addRoute(address, prefixLength); mCache.addRoute(address, prefixLength);
} }
catch (IllegalArgumentException ex) catch (IllegalArgumentException ex)
@@ -611,7 +626,6 @@ public class CharonVpnService extends VpnService implements Runnable
{ {
try try
{ {
mBuilder.setMtu(mtu);
mCache.setMtu(mtu); mCache.setMtu(mtu);
} }
catch (IllegalArgumentException ex) catch (IllegalArgumentException ex)
@@ -626,6 +640,7 @@ public class CharonVpnService extends VpnService implements Runnable
ParcelFileDescriptor fd; ParcelFileDescriptor fd;
try try
{ {
mCache.applyData(mBuilder);
fd = mBuilder.establish(); fd = mBuilder.establish();
} }
catch (Exception ex) catch (Exception ex)
@@ -641,7 +656,7 @@ public class CharonVpnService extends VpnService implements Runnable
* builder anymore, but we might need another when reestablishing */ * builder anymore, but we might need another when reestablishing */
mBuilder = createBuilder(mName); mBuilder = createBuilder(mName);
mEstablishedCache = mCache; mEstablishedCache = mCache;
mCache = new BuilderCache(); mCache = new BuilderCache(mSplitTunneling);
return fd.detachFd(); return fd.detachFd();
} }
@@ -679,17 +694,40 @@ public class CharonVpnService extends VpnService implements Runnable
public class BuilderCache public class BuilderCache
{ {
private final List<PrefixedAddress> mAddresses = new ArrayList<PrefixedAddress>(); private final List<PrefixedAddress> mAddresses = new ArrayList<PrefixedAddress>();
private final List<PrefixedAddress> mRoutes = new ArrayList<PrefixedAddress>(); private final List<PrefixedAddress> mRoutesIPv4 = new ArrayList<PrefixedAddress>();
private final List<PrefixedAddress> mRoutesIPv6 = new ArrayList<PrefixedAddress>();
private final int mSplitTunneling;
private int mMtu; private int mMtu;
private boolean mIPv4Seen, mIPv6Seen;
public BuilderCache(Integer splitTunneling)
{
mSplitTunneling = splitTunneling != null ? splitTunneling : 0;
}
public void addAddress(String address, int prefixLength) public void addAddress(String address, int prefixLength)
{ {
mAddresses.add(new PrefixedAddress(address, prefixLength)); mAddresses.add(new PrefixedAddress(address, prefixLength));
recordAddressFamily(address);
} }
public void addRoute(String address, int prefixLength) public void addRoute(String address, int prefixLength)
{ {
mRoutes.add(new PrefixedAddress(address, prefixLength)); try
{
if (isIPv6(address))
{
mRoutesIPv6.add(new PrefixedAddress(address, prefixLength));
}
else
{
mRoutesIPv4.add(new PrefixedAddress(address, prefixLength));
}
}
catch (UnknownHostException ex)
{
ex.printStackTrace();
}
} }
public void setMtu(int mtu) public void setMtu(int mtu)
@@ -697,19 +735,89 @@ public class CharonVpnService extends VpnService implements Runnable
mMtu = mtu; mMtu = mtu;
} }
public void recordAddressFamily(String address)
{
try
{
if (isIPv6(address))
{
mIPv6Seen = true;
}
else
{
mIPv4Seen = true;
}
}
catch (UnknownHostException ex)
{
ex.printStackTrace();
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void applyData(VpnService.Builder builder) public void applyData(VpnService.Builder builder)
{ {
for (PrefixedAddress address : mAddresses) for (PrefixedAddress address : mAddresses)
{ {
builder.addAddress(address.mAddress, address.mPrefix); builder.addAddress(address.mAddress, address.mPrefix);
} }
for (PrefixedAddress route : mRoutes) /* add routes depending on whether split tunneling is allowed or not,
* that is, whether we have to handle and block non-VPN traffic */
if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) == 0)
{ {
builder.addRoute(route.mAddress, route.mPrefix); if (mIPv4Seen)
{ /* split tunneling is used depending on the routes */
for (PrefixedAddress route : mRoutesIPv4)
{
builder.addRoute(route.mAddress, route.mPrefix);
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{ /* allow traffic that would otherwise be blocked to bypass the VPN */
builder.allowFamily(OsConstants.AF_INET);
}
}
else if (mIPv4Seen)
{ /* only needed if we've seen any addresses. otherwise, traffic
* is blocked by default (we also install no routes in that case) */
builder.addRoute("0.0.0.0", 0);
}
/* same thing for IPv6 */
if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) == 0)
{
if (mIPv6Seen)
{
for (PrefixedAddress route : mRoutesIPv6)
{
builder.addRoute(route.mAddress, route.mPrefix);
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
builder.allowFamily(OsConstants.AF_INET6);
}
}
else if (mIPv6Seen)
{
builder.addRoute("::", 0);
} }
builder.setMtu(mMtu); builder.setMtu(mMtu);
} }
private boolean isIPv6(String address) throws UnknownHostException
{
InetAddress addr = InetAddress.getByName(address);
if (addr instanceof Inet4Address)
{
return false;
}
else if (addr instanceof Inet6Address)
{
return true;
}
return false;
}
private class PrefixedAddress private class PrefixedAddress
{ {
public String mAddress; public String mAddress;
@@ -725,22 +833,25 @@ public class CharonVpnService extends VpnService implements Runnable
/* /*
* The libraries are extracted to /data/data/org.strongswan.android/... * The libraries are extracted to /data/data/org.strongswan.android/...
* during installation. * during installation. On newer releases most are loaded in JNI_OnLoad.
*/ */
static static
{ {
System.loadLibrary("strongswan"); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
if (MainActivity.USE_BYOD)
{ {
System.loadLibrary("tncif"); System.loadLibrary("strongswan");
System.loadLibrary("tnccs");
System.loadLibrary("imcv");
}
System.loadLibrary("hydra"); if (MainActivity.USE_BYOD)
System.loadLibrary("charon"); {
System.loadLibrary("ipsec"); System.loadLibrary("tncif");
System.loadLibrary("tnccs");
System.loadLibrary("imcv");
}
System.loadLibrary("hydra");
System.loadLibrary("charon");
System.loadLibrary("ipsec");
}
System.loadLibrary("androidbridge"); System.loadLibrary("androidbridge");
} }
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil * Hochschule fuer Technik Rapperswil
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -42,12 +42,21 @@ public class NetworkManager extends BroadcastReceiver
mContext.unregisterReceiver(this); mContext.unregisterReceiver(this);
} }
public boolean isConnected()
{
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = null;
if (cm != null)
{
info = cm.getActiveNetworkInfo();
}
return info != null && info.isConnected();
}
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); networkChanged(!isConnected());
NetworkInfo info = cm.getActiveNetworkInfo();
networkChanged(info == null || !info.isConnected());
} }
/** /**
@@ -24,6 +24,7 @@ import org.strongswan.android.logic.imc.ImcState;
import org.strongswan.android.logic.imc.RemediationInstruction; import org.strongswan.android.logic.imc.RemediationInstruction;
import android.app.Fragment; import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.app.Service; import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
@@ -82,7 +83,7 @@ public class ImcStateFragment extends Fragment implements VpnStateListener
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) Bundle savedInstanceState)
{ {
View view = inflater.inflate(R.layout.imc_state_fragment, null); View view = inflater.inflate(R.layout.imc_state_fragment, container, false);
mButton = (LinearLayout)view.findViewById(R.id.imc_state_button); mButton = (LinearLayout)view.findViewById(R.id.imc_state_button);
mButton.setOnClickListener(new OnClickListener() { mButton.setOnClickListener(new OnClickListener() {
@@ -174,7 +175,12 @@ public class ImcStateFragment extends Fragment implements VpnStateListener
public void updateView() public void updateView()
{ {
FragmentTransaction ft = getFragmentManager().beginTransaction(); FragmentManager fm = getFragmentManager();
if (fm == null)
{
return;
}
FragmentTransaction ft = fm.beginTransaction();
switch (mService.getImcState()) switch (mService.getImcState())
{ {
@@ -60,6 +60,8 @@ import android.widget.TextView;
public class VpnProfileDetailActivity extends Activity public class VpnProfileDetailActivity extends Activity
{ {
private static final int SELECT_TRUSTED_CERTIFICATE = 0; private static final int SELECT_TRUSTED_CERTIFICATE = 0;
private static final int MTU_MIN = 1280;
private static final int MTU_MAX = 1500;
private VpnProfileDataSource mDataSource; private VpnProfileDataSource mDataSource;
private Long mId; private Long mId;
@@ -79,6 +81,12 @@ public class VpnProfileDetailActivity extends Activity
private CheckBox mCheckAuto; private CheckBox mCheckAuto;
private RelativeLayout mSelectCert; private RelativeLayout mSelectCert;
private RelativeLayout mTncNotice; private RelativeLayout mTncNotice;
private CheckBox mShowAdvanced;
private ViewGroup mAdvancedSettings;
private EditText mMTU;
private EditText mPort;
private CheckBox mBlockIPv4;
private CheckBox mBlockIPv6;
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
@@ -108,6 +116,14 @@ public class VpnProfileDetailActivity extends Activity
mCheckAuto = (CheckBox)findViewById(R.id.ca_auto); mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate); mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate);
mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
mMTU = (EditText)findViewById(R.id.mtu);
mPort = (EditText)findViewById(R.id.port);
mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() { mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
@@ -154,6 +170,14 @@ public class VpnProfileDetailActivity extends Activity
} }
}); });
mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
updateAdvancedSettings();
}
});
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID); mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
if (mId == null) if (mId == null)
{ {
@@ -165,6 +189,7 @@ public class VpnProfileDetailActivity extends Activity
updateCredentialView(); updateCredentialView();
updateCertificateSelector(); updateCertificateSelector();
updateAdvancedSettings();
} }
@Override @Override
@@ -314,6 +339,22 @@ public class VpnProfileDetailActivity extends Activity
} }
} }
/**
* Update the advanced settings UI depending on whether any advanced
* settings have already been made.
*/
private void updateAdvancedSettings()
{
boolean show = mShowAdvanced.isChecked();
if (!show && mProfile != null)
{
Integer st = mProfile.getSplitTunneling();
show = mProfile.getMTU() != null || mProfile.getPort() != null || (st != null && st != 0);
}
mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
}
/** /**
* Save or update the profile depending on whether we actually have a * Save or update the profile depending on whether we actually have a
* profile object or not (this was created in updateProfileData) * profile object or not (this was created in updateProfileData)
@@ -368,6 +409,18 @@ public class VpnProfileDetailActivity extends Activity
showCertificateAlert(); showCertificateAlert();
valid = false; valid = false;
} }
Integer mtu = getInteger(mMTU);
if (mtu != null && (mtu < MTU_MIN || mtu > MTU_MAX))
{
mMTU.setError(String.format(getString(R.string.alert_text_out_of_range), MTU_MIN, MTU_MAX));
valid = false;
}
Integer port = getInteger(mPort);
if (port != null && (port < 1 || port > 65535))
{
mPort.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535));
valid = false;
}
return valid; return valid;
} }
@@ -395,6 +448,12 @@ public class VpnProfileDetailActivity extends Activity
} }
String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias(); String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias();
mProfile.setCertificateAlias(certAlias); mProfile.setCertificateAlias(certAlias);
mProfile.setMTU(getInteger(mMTU));
mProfile.setPort(getInteger(mPort));
int st = 0;
st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
mProfile.setSplitTunneling(st == 0 ? null : st);
} }
/** /**
@@ -417,6 +476,10 @@ public class VpnProfileDetailActivity extends Activity
mVpnType = mProfile.getVpnType(); mVpnType = mProfile.getVpnType();
mUsername.setText(mProfile.getUsername()); mUsername.setText(mProfile.getUsername());
mPassword.setText(mProfile.getPassword()); mPassword.setText(mProfile.getPassword());
mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null);
mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null);
mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0 : false);
mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0 : false);
useralias = mProfile.getUserCertificateAlias(); useralias = mProfile.getUserCertificateAlias();
alias = mProfile.getCertificateAlias(); alias = mProfile.getCertificateAlias();
getActionBar().setTitle(mProfile.getName()); getActionBar().setTitle(mProfile.getName());
@@ -458,6 +521,17 @@ public class VpnProfileDetailActivity extends Activity
} }
} }
/**
* Get the integer value in the given text box or null if empty
*
* @param view text box (numeric entry assumed)
*/
private Integer getInteger(EditText view)
{
String value = view.getText().toString().trim();
return value.isEmpty() ? null : Integer.valueOf(value);
}
private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback
{ {
@Override @Override
@@ -0,0 +1,160 @@
/*
* Copyright (C) 2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package org.strongswan.android.utils;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.regex.Pattern;
/**
* Simple generator for data/files that may be parsed by libstrongswan's
* settings_t class.
*/
public class SettingsWriter
{
/**
* Top-level section
*/
private final SettingsSection mTop = new SettingsSection();
/**
* Set a string value
* @param key
* @param value
* @return the writer
*/
public SettingsWriter setValue(String key, String value)
{
Pattern pattern = Pattern.compile("[^#{}=\"\\n\\t ]+");
if (key == null || !pattern.matcher(key).matches())
{
return this;
}
String[] keys = key.split("\\.");
SettingsSection section = mTop;
section = findOrCreateSection(Arrays.copyOfRange(keys, 0, keys.length-1));
section.Settings.put(keys[keys.length-1], value);
return this;
}
/**
* Set an integer value
* @param key
* @param value
* @return the writer
*/
public SettingsWriter setValue(String key, Integer value)
{
return setValue(key, value == null ? null : value.toString());
}
/**
* Set a boolean value
* @param key
* @param value
* @return the writer
*/
public SettingsWriter setValue(String key, Boolean value)
{
return setValue(key, value == null ? null : value ? "1" : "0");
}
/**
* Serializes the settings to a string in the format understood by
* libstrongswan's settings_t parser.
* @return serialized settings
*/
public String serialize()
{
StringBuilder builder = new StringBuilder();
serializeSection(mTop, builder);
return builder.toString();
}
/**
* Serialize the settings in a section and recursively serialize sub-sections
* @param section
* @param builder
*/
private void serializeSection(SettingsSection section, StringBuilder builder)
{
for (Entry<String, String> setting : section.Settings.entrySet())
{
builder.append(setting.getKey()).append('=');
if (setting.getValue() != null)
{
builder.append("\"").append(escapeValue(setting.getValue())).append("\"");
}
builder.append('\n');
}
for (Entry<String, SettingsSection> subsection : section.Sections.entrySet())
{
builder.append(subsection.getKey()).append(" {\n");
serializeSection(subsection.getValue(), builder);
builder.append("}\n");
}
}
/**
* Escape value so it may be wrapped in "
* @param value
* @return
*/
private String escapeValue(String value)
{
return value.replace("\"", "\\\"");
}
/**
* Find or create the nested sections with the given names
* @param sections list of section names
* @return final section
*/
private SettingsSection findOrCreateSection(String[] sections)
{
SettingsSection section = mTop;
for (String name : sections)
{
SettingsSection subsection = section.Sections.get(name);
if (subsection == null)
{
subsection = new SettingsSection();
section.Sections.put(name, subsection);
}
section = subsection;
}
return section;
}
/**
* A section containing sub-sections and settings.
*/
private class SettingsSection
{
/**
* Assigned key/value pairs
*/
LinkedHashMap<String,String> Settings = new LinkedHashMap<String, String>();
/**
* Assigned sub-sections
*/
LinkedHashMap<String,SettingsSection> Sections = new LinkedHashMap<String, SettingsWriter.SettingsSection>();
}
}
+1 -2
View File
@@ -1,3 +1,2 @@
EXTRA_DIST = linux/if_alg.h linux/ipsec.h linux/netlink.h linux/rtnetlink.h \ EXTRA_DIST = linux/if_alg.h linux/ipsec.h linux/netlink.h linux/rtnetlink.h \
linux/pfkeyv2.h linux/udp.h linux/xfrm.h linux/types.h \ linux/pfkeyv2.h linux/udp.h linux/xfrm.h sys/queue.h
sys/queue.h
-172
View File
@@ -1,172 +0,0 @@
#ifndef _LINUX_TYPES_H
#define _LINUX_TYPES_H
#include <linux/posix_types.h>
#include <asm/types.h>
#ifndef __KERNEL_STRICT_NAMES
typedef __u32 __kernel_dev_t;
typedef __kernel_fd_set fd_set;
typedef __kernel_dev_t dev_t;
typedef __kernel_ino_t ino_t;
typedef __kernel_mode_t mode_t;
typedef __kernel_nlink_t nlink_t;
typedef __kernel_off_t off_t;
typedef __kernel_pid_t pid_t;
typedef __kernel_daddr_t daddr_t;
typedef __kernel_key_t key_t;
typedef __kernel_suseconds_t suseconds_t;
typedef __kernel_timer_t timer_t;
typedef __kernel_clockid_t clockid_t;
typedef __kernel_mqd_t mqd_t;
typedef __kernel_uid_t uid_t;
typedef __kernel_gid_t gid_t;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __kernel_loff_t loff_t;
#endif
/*
* The following typedefs are also protected by individual ifdefs for
* historical reasons:
*/
#ifndef _SIZE_T
#define _SIZE_T
typedef __kernel_size_t size_t;
#endif
#ifndef _SSIZE_T
#define _SSIZE_T
typedef __kernel_ssize_t ssize_t;
#endif
#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef __kernel_ptrdiff_t ptrdiff_t;
#endif
#ifndef _TIME_T
#define _TIME_T
typedef __kernel_time_t time_t;
#endif
#ifndef _CLOCK_T
#define _CLOCK_T
typedef __kernel_clock_t clock_t;
#endif
#ifndef _CADDR_T
#define _CADDR_T
typedef __kernel_caddr_t caddr_t;
#endif
/* bsd */
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
/* sysv */
typedef unsigned char unchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#ifndef __BIT_TYPES_DEFINED__
#define __BIT_TYPES_DEFINED__
typedef __u8 u_int8_t;
typedef __s8 int8_t;
typedef __u16 u_int16_t;
typedef __s16 int16_t;
typedef __u32 u_int32_t;
typedef __s32 int32_t;
#endif /* !(__BIT_TYPES_DEFINED__) */
typedef __u8 uint8_t;
typedef __u16 uint16_t;
typedef __u32 uint32_t;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __u64 uint64_t;
typedef __u64 u_int64_t;
typedef __s64 int64_t;
#endif
/* this is a special 64bit data type that is 8-byte aligned */
#define aligned_u64 unsigned long long __attribute__((aligned(8)))
#define aligned_be64 __be64 __attribute__((aligned(8)))
#define aligned_le64 __le64 __attribute__((aligned(8)))
/**
* The type used for indexing onto a disc or disc partition.
*
* Linux always considers sectors to be 512 bytes long independently
* of the devices real block size.
*/
#ifdef CONFIG_LBD
typedef u64 sector_t;
#else
typedef unsigned long sector_t;
#endif
/*
* The type of the inode's block count.
*/
#ifdef CONFIG_LSF
typedef u64 blkcnt_t;
#else
typedef unsigned long blkcnt_t;
#endif
/*
* The type of an index into the pagecache. Use a #define so asm/types.h
* can override it.
*/
#ifndef pgoff_t
#define pgoff_t unsigned long
#endif
#endif /* __KERNEL_STRICT_NAMES */
/*
* Below are truly Linux-specific types that should never collide with
* any application/library that wants linux/types.h.
*/
#ifdef __CHECKER__
#define __bitwise__ __attribute__((bitwise))
#else
#define __bitwise__
#endif
#ifdef __CHECK_ENDIAN__
#define __bitwise __bitwise__
#else
#define __bitwise
#endif
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;
#endif
typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;
struct ustat {
__kernel_daddr_t f_tfree;
__kernel_ino_t f_tinode;
char f_fname[6];
char f_fpack[6];
};
#endif /* _LINUX_TYPES_H */
+58 -11
View File
@@ -37,9 +37,10 @@
typedef struct private_settings_t private_settings_t; typedef struct private_settings_t private_settings_t;
/** /**
* Parse function provided by the generated parser. * Parse functions provided by the generated parser.
*/ */
bool settings_parser_parse_file(section_t *root, char *name); bool settings_parser_parse_file(section_t *root, char *name);
bool settings_parser_parse_string(section_t *root, char *settings);
/** /**
* Private data of settings * Private data of settings
@@ -843,16 +844,17 @@ METHOD(settings_t, add_fallback, void,
} }
/** /**
* Load settings from files matching the given file pattern. * Load settings from files matching the given file pattern or from a string.
* All sections and values are added relative to "parent". * All sections and values are added relative to "parent".
* All files (even included ones) have to be loaded successfully. * All files (even included ones) have to be loaded successfully.
* If merge is FALSE the contents of parent are replaced with the parsed * If merge is FALSE the contents of parent are replaced with the parsed
* contents, otherwise they are merged together. * contents, otherwise they are merged together.
*/ */
static bool load_files_internal(private_settings_t *this, section_t *parent, static bool load_internal(private_settings_t *this, section_t *parent,
char *pattern, bool merge) char *pattern, bool merge, bool string)
{ {
section_t *section; section_t *section;
bool loaded;
if (pattern == NULL || !pattern[0]) if (pattern == NULL || !pattern[0])
{ /* TODO: Clear parent if merge is FALSE? */ { /* TODO: Clear parent if merge is FALSE? */
@@ -860,7 +862,9 @@ static bool load_files_internal(private_settings_t *this, section_t *parent,
} }
section = settings_section_create(NULL); section = settings_section_create(NULL);
if (!settings_parser_parse_file(section, pattern)) loaded = string ? settings_parser_parse_string(section, pattern) :
settings_parser_parse_file(section, pattern);
if (!loaded)
{ {
settings_section_destroy(section, NULL); settings_section_destroy(section, NULL);
return FALSE; return FALSE;
@@ -877,7 +881,7 @@ static bool load_files_internal(private_settings_t *this, section_t *parent,
METHOD(settings_t, load_files, bool, METHOD(settings_t, load_files, bool,
private_settings_t *this, char *pattern, bool merge) private_settings_t *this, char *pattern, bool merge)
{ {
return load_files_internal(this, this->top, pattern, merge); return load_internal(this, this->top, pattern, merge, FALSE);
} }
METHOD(settings_t, load_files_section, bool, METHOD(settings_t, load_files_section, bool,
@@ -894,7 +898,30 @@ METHOD(settings_t, load_files_section, bool,
{ {
return FALSE; return FALSE;
} }
return load_files_internal(this, section, pattern, merge); return load_internal(this, section, pattern, merge, FALSE);
}
METHOD(settings_t, load_string, bool,
private_settings_t *this, char *settings, bool merge)
{
return load_internal(this, this->top, settings, merge, TRUE);
}
METHOD(settings_t, load_string_section, bool,
private_settings_t *this, char *settings, bool merge, char *key, ...)
{
section_t *section;
va_list args;
va_start(args, key);
section = ensure_section(this, this->top, key, args);
va_end(args);
if (!section)
{
return FALSE;
}
return load_internal(this, section, settings, merge, TRUE);
} }
METHOD(settings_t, destroy, void, METHOD(settings_t, destroy, void,
@@ -906,10 +933,7 @@ METHOD(settings_t, destroy, void,
free(this); free(this);
} }
/* static private_settings_t *settings_create_base()
* see header file
*/
settings_t *settings_create(char *file)
{ {
private_settings_t *this; private_settings_t *this;
@@ -931,14 +955,37 @@ settings_t *settings_create(char *file)
.add_fallback = _add_fallback, .add_fallback = _add_fallback,
.load_files = _load_files, .load_files = _load_files,
.load_files_section = _load_files_section, .load_files_section = _load_files_section,
.load_string = _load_string,
.load_string_section = _load_string_section,
.destroy = _destroy, .destroy = _destroy,
}, },
.top = settings_section_create(NULL), .top = settings_section_create(NULL),
.contents = array_create(0, 0), .contents = array_create(0, 0),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
); );
return this;
}
/*
* see header file
*/
settings_t *settings_create(char *file)
{
private_settings_t *this = settings_create_base();
load_files(this, file, FALSE); load_files(this, file, FALSE);
return &this->public; return &this->public;
} }
/*
* see header file
*/
settings_t *settings_create_string(char *settings)
{
private_settings_t *this = settings_create_base();
load_string(this, settings, FALSE);
return &this->public;
}
+54
View File
@@ -334,6 +334,50 @@ struct settings_t {
bool (*load_files_section)(settings_t *this, char *pattern, bool merge, bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
char *section, ...); char *section, ...);
/**
* Load settings from the given string.
*
* If merge is TRUE, existing sections are extended, existing values
* replaced, by those found in the string. If it is FALSE, existing
* sections are purged before reading the new config.
*
* @note If the string contains _include_ statements they should be
* absolute paths.
*
* @note If any failures occur, no settings are added at all. So, it's all
* or nothing.
*
* @param settings string to parse
* @param merge TRUE to merge config with existing values
* @return TRUE, if settings were loaded successfully
*/
bool (*load_string)(settings_t *this, char *settings, bool merge);
/**
* Load settings from the given string.
*
* If merge is TRUE, existing sections are extended, existing values
* replaced, by those found in the string. If it is FALSE, existing
* sections are purged before reading the new config.
*
* All settings are loaded relative to the given section. The section is
* created, if it does not yet exist.
*
* @note If the string contains _include_ statements they should be
* absolute paths.
*
* @note If any failures occur, no settings are added at all. So, it's all
* or nothing.
*
* @param settings string to parse
* @param merge TRUE to merge config with existing values
* @param section section name of parent section, printf style
* @param ... argument list for section
* @return TRUE, if settings were loaded successfully
*/
bool (*load_string_section)(settings_t *this, char *settings, bool merge,
char *section, ...);
/** /**
* Destroy a settings instance. * Destroy a settings instance.
*/ */
@@ -350,4 +394,14 @@ struct settings_t {
*/ */
settings_t *settings_create(char *file); settings_t *settings_create(char *file);
/**
* Load settings from a string.
*
* @note If parsing the file fails the object is still created.
*
* @param settings string to read settings from
* @return settings object, or NULL
*/
settings_t *settings_create_string(char *settings);
#endif /** SETTINGS_H_ @}*/ #endif /** SETTINGS_H_ @}*/
+10 -9
View File
@@ -119,16 +119,11 @@ static void include_files(parser_helper_t *ctx);
<str>{ <str>{
"\"" | "\"" |
<<EOF>> | <<EOF>> |
\n |
\\ { \\ {
if (!streq(yytext, "\"")) if (!streq(yytext, "\""))
{ {
if (streq(yytext, "\n"))
{ /* put the newline back to fix the line numbers */
unput('\n');
yy_set_bol(0);
}
PARSER_DBG1(yyextra, "unterminated string detected"); PARSER_DBG1(yyextra, "unterminated string detected");
return STRING_ERROR;
} }
if (yy_top_state(yyscanner) == inc) if (yy_top_state(yyscanner) == inc)
{ /* string include */ { /* string include */
@@ -146,11 +141,9 @@ static void include_files(parser_helper_t *ctx);
\\n yyextra->string_add(yyextra, "\n"); \\n yyextra->string_add(yyextra, "\n");
\\r yyextra->string_add(yyextra, "\r"); \\r yyextra->string_add(yyextra, "\r");
\\t yyextra->string_add(yyextra, "\t"); \\t yyextra->string_add(yyextra, "\t");
\\b yyextra->string_add(yyextra, "\b");
\\f yyextra->string_add(yyextra, "\f");
\\\r?\n /* merge lines that end with EOL characters */ \\\r?\n /* merge lines that end with EOL characters */
\\. yyextra->string_add(yyextra, yytext+1); \\. yyextra->string_add(yyextra, yytext+1);
[^\\\n"]+ { [^\\"]+ {
yyextra->string_add(yyextra, yytext); yyextra->string_add(yyextra, yytext);
} }
} }
@@ -198,3 +191,11 @@ static void include_files(parser_helper_t *ctx)
settings_parser_open_next_file(ctx); settings_parser_open_next_file(ctx);
} }
/**
* Load the given string to be parsed next
*/
void settings_parser_load_string(parser_helper_t *ctx, const char *content)
{
settings_parser__scan_string(content, ctx->scanner);
}
+38 -1
View File
@@ -39,6 +39,7 @@ int settings_parser_get_leng(void *scanner);
int settings_parser_get_lineno(void *scanner); int settings_parser_get_lineno(void *scanner);
/* Custom functions in lexer */ /* Custom functions in lexer */
bool settings_parser_open_next_file(parser_helper_t *ctx); bool settings_parser_open_next_file(parser_helper_t *ctx);
bool settings_parser_load_string(parser_helper_t *ctx, const char *content);
/** /**
* Forward declarations * Forward declarations
@@ -79,7 +80,7 @@ static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
struct kv_t *kv; struct kv_t *kv;
} }
%token <s> NAME STRING %token <s> NAME STRING
%token NEWLINE %token NEWLINE STRING_ERROR
/* ...and other symbols */ /* ...and other symbols */
%type <s> value valuepart %type <s> value valuepart
@@ -286,3 +287,39 @@ bool settings_parser_parse_file(section_t *root, char *name)
helper->destroy(helper); helper->destroy(helper);
return success; return success;
} }
/**
* Parse the given string and add all sections and key/value pairs to the
* given section.
*/
bool settings_parser_parse_string(section_t *root, char *settings)
{
parser_helper_t *helper;
array_t *sections = NULL;
bool success = FALSE;
array_insert_create(&sections, ARRAY_TAIL, root);
helper = parser_helper_create(sections);
helper->get_lineno = settings_parser_get_lineno;
if (settings_parser_lex_init_extra(helper, &helper->scanner) != 0)
{
helper->destroy(helper);
array_destroy(sections);
return FALSE;
}
settings_parser_load_string(helper, settings);
if (getenv("DEBUG_SETTINGS_PARSER"))
{
yydebug = 1;
settings_parser_set_debug(1, helper->scanner);
}
success = yyparse(helper) == 0;
if (!success)
{
DBG1(DBG_CFG, "failed to parse settings '%s'", settings);
}
array_destroy(sections);
settings_parser_lex_destroy(helper->scanner);
helper->destroy(helper);
return success;
}
+130 -16
View File
@@ -58,6 +58,10 @@ START_SETUP(setup_base_config)
" }\n" " }\n"
" key2 = with space\n" " key2 = with space\n"
" key3 = \"string with\\nnewline\"\n" " key3 = \"string with\\nnewline\"\n"
" key4 = \"multi line\n"
"string\"\n"
" key5 = \"escaped \\\n"
"newline\"\n"
"}\n" "}\n"
"out = side\n" "out = side\n"
"other {\n" "other {\n"
@@ -88,6 +92,8 @@ START_TEST(test_get_str)
verify_string("", "main.empty"); verify_string("", "main.empty");
verify_string("with space", "main.key2"); verify_string("with space", "main.key2");
verify_string("string with\nnewline", "main.key3"); verify_string("string with\nnewline", "main.key3");
verify_string("multi line\nstring", "main.key4");
verify_string("escaped newline", "main.key5");
verify_string("value", "main.sub1.key"); verify_string("value", "main.sub1.key");
verify_string("value2", "main.sub1.key2"); verify_string("value2", "main.sub1.key2");
verify_string("bar", "main.sub1.subsub.foo"); verify_string("bar", "main.sub1.subsub.foo");
@@ -97,7 +103,7 @@ START_TEST(test_get_str)
verify_string("other val", "other.key1"); verify_string("other val", "other.key1");
verify_null("main.none"); verify_null("main.none");
verify_null("main.key4"); verify_null("main.key6");
verify_null("other.sub"); verify_null("other.sub");
} }
END_TEST END_TEST
@@ -131,7 +137,7 @@ START_TEST(test_get_str_printf)
* probably document it at least */ * probably document it at least */
verify_null("main.%s%u.key%d", "sub", 1, 2); verify_null("main.%s%u.key%d", "sub", 1, 2);
verify_null("%s.%s%d", "main", "key", 4); verify_null("%s.%s%d", "main", "key", 6);
} }
END_TEST END_TEST
@@ -529,9 +535,7 @@ END_TEST
# define include2 "/tmp/strongswan-settings-test-include2" # define include2 "/tmp/strongswan-settings-test-include2"
#endif #endif
START_SETUP(setup_include_config) static char *include_content1 =
{
chunk_t inc1 = chunk_from_str(
"main {\n" "main {\n"
" key1 = n1\n" " key1 = n1\n"
" key2 = n2\n" " key2 = n2\n"
@@ -544,14 +548,17 @@ START_SETUP(setup_include_config)
" sub3 = val3\n" " sub3 = val3\n"
" }\n" " }\n"
" include " include2 "\n" " include " include2 "\n"
"}"); "}";
chunk_t inc2 = chunk_from_str( static char *include_content2 =
"key2 = v2\n" "key2 = v2\n"
"sub1 {\n" "sub1 {\n"
" key = val\n" " key = val\n"
"}"); "}";
ck_assert(chunk_write(inc1, include1, 0022, TRUE));
ck_assert(chunk_write(inc2, include2, 0022, TRUE)); START_SETUP(setup_include_config)
{
ck_assert(chunk_write(chunk_from_str(include_content1), include1, 0022, TRUE));
ck_assert(chunk_write(chunk_from_str(include_content2), include2, 0022, TRUE));
} }
END_SETUP END_SETUP
@@ -784,6 +791,104 @@ START_TEST(test_order_section)
} }
END_TEST END_TEST
START_TEST(test_load_string)
{
char *content =
"main {\n"
" key1 = val1\n"
" key2 = val2\n"
" key3 = val3\n"
" none = x\n"
" sub1 {\n"
" include = value\n"
" key2 = v2\n"
" sub1 {\n"
" key = val\n"
" }\n"
" }\n"
"}";
char *val1, *val2, *val3;
settings = settings_create_string(content);
val1 = settings->get_str(settings, "main.key1", NULL);
val2 = settings->get_str(settings, "main.sub1.key2", NULL);
/* loading the same content twice should not change anything, with... */
ck_assert(settings->load_string(settings, content, TRUE));
ck_assert(val1 == settings->get_str(settings, "main.key1", NULL));
ck_assert(val2 == settings->get_str(settings, "main.sub1.key2", NULL));
/* ...or without merging */
ck_assert(settings->load_string(settings, content, FALSE));
ck_assert(val1 == settings->get_str(settings, "main.key1", NULL));
ck_assert(val2 == settings->get_str(settings, "main.sub1.key2", NULL));
val1 = settings->get_str(settings, "main.key2", NULL);
val2 = settings->get_str(settings, "main.key3", NULL);
val3 = settings->get_str(settings, "main.none", NULL);
/* only pointers for modified settings should change, but still be valid */
ck_assert(settings->load_string(settings, include_content1, FALSE));
ck_assert(val1 != settings->get_str(settings, "main.key2", NULL));
ck_assert_str_eq(val1, "val2");
ck_assert(val2 == settings->get_str(settings, "main.key3", NULL));
ck_assert(val3 != settings->get_str(settings, "main.none", NULL));
ck_assert_str_eq(val3, "x");
settings->destroy(settings);
settings = settings_create_string(content);
ck_assert(settings);
ck_assert(settings->load_string(settings, include_content1, TRUE));
verify_include();
ck_assert(settings->load_string(settings, include_content2, FALSE));
verify_null("main.key1");
verify_string("v2", "key2");
verify_string("val", "sub1.key");
verify_null("main.sub1.key3");
}
END_TEST
START_TEST(test_load_string_section)
{
char *content =
"main {\n"
" key1 = val1\n"
" key2 = val2\n"
" none = x\n"
" sub1 {\n"
" include = value\n"
" key2 = value2\n"
" }\n"
"}";
settings = settings_create_string(content);
ck_assert(settings->load_string_section(settings, include_content1, TRUE, ""));
ck_assert(settings->load_string_section(settings, include_content2, TRUE, "main.sub1"));
verify_include();
/* invalid strings are a failure */
ck_assert(!settings->load_string_section(settings, "conf {", TRUE, ""));
/* NULL or empty strings are OK though */
ck_assert(settings->load_string_section(settings, "", TRUE, ""));
ck_assert(settings->load_string_section(settings, NULL, TRUE, ""));
verify_include();
ck_assert(settings->load_string_section(settings, include_content2, FALSE, "main"));
verify_null("main.key1");
verify_string("v2", "main.key2");
verify_string("val", "main.sub1.key");
verify_null("main.sub1.key3");
verify_null("main.sub2.sub3");
ck_assert(settings->load_string_section(settings, include_content2, TRUE, "main.sub2"));
verify_string("v2", "main.sub2.key2");
verify_string("val", "main.sub2.sub1.key");
}
END_TEST
START_SETUP(setup_fallback_config) START_SETUP(setup_fallback_config)
{ {
create_settings(chunk_from_str( create_settings(chunk_from_str(
@@ -906,9 +1011,8 @@ START_SETUP(setup_string_config)
create_settings(chunk_from_str( create_settings(chunk_from_str(
"string = \" with accurate\twhitespace\"\n" "string = \" with accurate\twhitespace\"\n"
"special = \"all { special } characters # can be used.\"\n" "special = \"all { special } characters # can be used.\"\n"
"unterminated = \"is fine\n" "newlines = \"can be encoded explicitly\\nor implicitly\n"
"but = produces a warning\n" "or \\\n"
"newlines = \"can either be encoded\\nor \\\n"
"escaped\"\n" "escaped\"\n"
"quotes = \"\\\"and\\\" slashes \\\\ can \\\\ be\" # escaped too\n" "quotes = \"\\\"and\\\" slashes \\\\ can \\\\ be\" # escaped too\n"
"multiple = \"strings\" are \"combined\"\n" "multiple = \"strings\" are \"combined\"\n"
@@ -920,9 +1024,7 @@ START_TEST(test_strings)
{ {
verify_string(" with accurate\twhitespace", "string"); verify_string(" with accurate\twhitespace", "string");
verify_string("all { special } characters # can be used.", "special"); verify_string("all { special } characters # can be used.", "special");
verify_string("is fine", "unterminated"); verify_string("can be encoded explicitly\nor implicitly\nor escaped", "newlines");
verify_string("produces a warning", "but");
verify_string("can either be encoded\nor escaped", "newlines");
verify_string("\"and\" slashes \\ can \\ be", "quotes"); verify_string("\"and\" slashes \\ can \\ be", "quotes");
verify_string("strings are combined", "multiple"); verify_string("strings are combined", "multiple");
} }
@@ -989,6 +1091,12 @@ START_TEST(test_invalid)
ck_assert(chunk_write(contents, path, 0022, TRUE)); ck_assert(chunk_write(contents, path, 0022, TRUE));
ck_assert(!settings->load_files(settings, path, FALSE)); ck_assert(!settings->load_files(settings, path, FALSE));
contents = chunk_from_str(
"unterminated {\n"
" strings = \"are invalid\n");
ck_assert(chunk_write(contents, path, 0022, TRUE));
ck_assert(!settings->load_files(settings, path, FALSE));
contents = chunk_from_str( contents = chunk_from_str(
"spaces in name {}"); "spaces in name {}");
ck_assert(chunk_write(contents, path, 0022, TRUE)); ck_assert(chunk_write(contents, path, 0022, TRUE));
@@ -1060,6 +1168,12 @@ Suite *settings_suite_create()
tcase_add_test(tc, test_order_section); tcase_add_test(tc, test_order_section);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("load_string[_section]");
tcase_add_checked_fixture(tc, setup_include_config, teardown_config);
tcase_add_test(tc, test_load_string);
tcase_add_test(tc, test_load_string_section);
suite_add_tcase(s, tc);
tc = tcase_create("fallback"); tc = tcase_create("fallback");
tcase_add_checked_fixture(tc, setup_fallback_config, teardown_config); tcase_add_checked_fixture(tc, setup_fallback_config, teardown_config);
tcase_add_test(tc, test_add_fallback); tcase_add_test(tc, test_add_fallback);
+2 -9
View File
@@ -123,16 +123,11 @@ static void include_files(parser_helper_t *ctx);
<str>{ <str>{
"\"" | "\"" |
<<EOF>> | <<EOF>> |
\n |
\\ { \\ {
if (!streq(yytext, "\"")) if (!streq(yytext, "\""))
{ {
if (streq(yytext, "\n"))
{ /* put the newline back to fix the line numbers */
unput('\n');
yy_set_bol(0);
}
PARSER_DBG1(yyextra, "unterminated string detected"); PARSER_DBG1(yyextra, "unterminated string detected");
return STRING_ERROR;
} }
if (yy_top_state(yyscanner) == inc) if (yy_top_state(yyscanner) == inc)
{ /* string include */ { /* string include */
@@ -150,11 +145,9 @@ static void include_files(parser_helper_t *ctx);
\\n yyextra->string_add(yyextra, "\n"); \\n yyextra->string_add(yyextra, "\n");
\\r yyextra->string_add(yyextra, "\r"); \\r yyextra->string_add(yyextra, "\r");
\\t yyextra->string_add(yyextra, "\t"); \\t yyextra->string_add(yyextra, "\t");
\\b yyextra->string_add(yyextra, "\b");
\\f yyextra->string_add(yyextra, "\f");
\\\r?\n /* merge lines that end with EOL characters */ \\\r?\n /* merge lines that end with EOL characters */
\\. yyextra->string_add(yyextra, yytext+1); \\. yyextra->string_add(yyextra, yytext+1);
[^\\\n"]+ { [^\\"]+ {
yyextra->string_add(yyextra, yytext); yyextra->string_add(yyextra, yytext);
} }
} }
+1 -1
View File
@@ -73,7 +73,7 @@ static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
conf_parser_section_t t; conf_parser_section_t t;
} }
%token <s> STRING %token <s> STRING
%token EQ SPACES NEWLINE CONFIG_SETUP CONN CA %token EQ SPACES NEWLINE CONFIG_SETUP CONN CA STRING_ERROR
/* ...and other symbols */ /* ...and other symbols */
%type <t> section_type %type <t> section_type
+3
View File
@@ -328,6 +328,9 @@ static struct {
{ TRUE, "conn foo\n\tkey=val ue", "foo", "val ue" }, { TRUE, "conn foo\n\tkey=val ue", "foo", "val ue" },
{ TRUE, "conn foo\n\tkey=\"val ue\"", "foo", "val ue" }, { TRUE, "conn foo\n\tkey=\"val ue\"", "foo", "val ue" },
{ TRUE, "conn foo\n\tkey=\"val\\nue\"", "foo", "val\nue" }, { TRUE, "conn foo\n\tkey=\"val\\nue\"", "foo", "val\nue" },
{ TRUE, "conn foo\n\tkey=\"val\nue\"", "foo", "val\nue" },
{ TRUE, "conn foo\n\tkey=\"val\\\nue\"", "foo", "value" },
{ FALSE, "conn foo\n\tkey=\"unterminated", "foo", NULL },
}; };
START_TEST(test_strings) START_TEST(test_strings)