2 * Copyright (C) 2012 Giuliano Grassi
3 * Copyright (C) 2012 Ralf Sager
4 * Copyright (C) 2012 Tobias Brunner
5 * Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 #include <sys/utsname.h>
21 #include <android/log.h>
23 #include "charonservice.h"
24 #include "android_jni.h"
25 #include "backend/android_attr.h"
26 #include "backend/android_creds.h"
27 #include "backend/android_service.h"
28 #include "kernel/android_ipsec.h"
29 #include "kernel/android_net.h"
35 #include <threading/thread.h>
37 #define ANDROID_DEBUG_LEVEL 1
38 #define ANDROID_RETRASNMIT_TRIES 3
39 #define ANDROID_RETRANSMIT_TIMEOUT 3.0
40 #define ANDROID_RETRANSMIT_BASE 1.4
42 typedef struct private_charonservice_t private_charonservice_t
;
45 * private data of charonservice
47 struct private_charonservice_t
{
52 charonservice_t
public;
55 * android_attr instance
60 * android_creds instance
62 android_creds_t
*creds
;
65 * android_service instance
67 android_service_t
*service
;
70 * VpnService builder (accessed via JNI)
72 vpnservice_builder_t
*builder
;
75 * CharonVpnService reference
81 * Single instance of charonservice_t.
83 charonservice_t
*charonservice
;
86 * hook in library for debugging messages
88 extern void (*dbg
)(debug_t group
, level_t level
, char *fmt
, ...);
91 * Logging hook for library logs, using android specific logging
93 static void dbg_android(debug_t group
, level_t level
, char *fmt
, ...)
97 if (level
<= ANDROID_DEBUG_LEVEL
)
99 char sgroup
[16], buffer
[8192];
100 char *current
= buffer
, *next
;
102 snprintf(sgroup
, sizeof(sgroup
), "%N", debug_names
, group
);
104 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
107 { /* log each line separately */
108 next
= strchr(current
, '\n');
113 __android_log_print(ANDROID_LOG_INFO
, "charon", "00[%s] %s\n",
120 METHOD(charonservice_t
, update_status
, bool,
121 private_charonservice_t
*this, android_vpn_state_t code
)
125 bool success
= FALSE
;
127 androidjni_attach_thread(&env
);
129 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
130 "updateStatus", "(I)V");
135 (*env
)->CallVoidMethod(env
, this->vpn_service
, method_id
, (jint
)code
);
136 success
= !androidjni_exception_occurred(env
);
139 androidjni_exception_occurred(env
);
140 androidjni_detach_thread();
144 METHOD(charonservice_t
, bypass_socket
, bool,
145 private_charonservice_t
*this, int fd
, int family
)
150 androidjni_attach_thread(&env
);
152 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
158 if (!(*env
)->CallBooleanMethod(env
, this->vpn_service
, method_id
, fd
))
160 DBG1(DBG_CFG
, "VpnService.protect() failed");
163 androidjni_detach_thread();
167 androidjni_exception_occurred(env
);
168 androidjni_detach_thread();
172 METHOD(charonservice_t
, get_trusted_certificates
, linked_list_t
*,
173 private_charonservice_t
*this)
181 androidjni_attach_thread(&env
);
183 method_id
= (*env
)->GetMethodID(env
,
184 android_charonvpnservice_class
,
185 "getTrustedCertificates", "(Ljava/lang/String;)[[B");
190 jcerts
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
, NULL
);
195 list
= linked_list_create();
196 for (i
= 0; i
< (*env
)->GetArrayLength(env
, jcerts
); ++i
)
201 ca_cert
= malloc_thing(chunk_t
);
202 list
->insert_last(list
, ca_cert
);
204 jcert
= (*env
)->GetObjectArrayElement(env
, jcerts
, i
);
205 *ca_cert
= chunk_alloc((*env
)->GetArrayLength(env
, jcert
));
206 (*env
)->GetByteArrayRegion(env
, jcert
, 0, ca_cert
->len
, ca_cert
->ptr
);
207 (*env
)->DeleteLocalRef(env
, jcert
);
209 (*env
)->DeleteLocalRef(env
, jcerts
);
210 androidjni_detach_thread();
214 androidjni_exception_occurred(env
);
215 androidjni_detach_thread();
219 METHOD(charonservice_t
, get_vpnservice_builder
, vpnservice_builder_t
*,
220 private_charonservice_t
*this)
222 return this->builder
;
226 * Initiate a new connection
228 * @param local local ip address (gets owned)
229 * @param gateway gateway address (gets owned)
230 * @param username username (gets owned)
231 * @param password password (gets owned)
233 static void initiate(char *local
, char *gateway
, char *username
, char *password
)
235 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
237 this->creds
->clear(this->creds
);
238 this->creds
->add_username_password(this->creds
, username
, password
);
239 memwipe(password
, strlen(password
));
242 DESTROY_IF(this->service
);
243 this->service
= android_service_create(local
, gateway
, username
);
247 * Initialize/deinitialize Android backend
249 static bool charonservice_register(void *plugin
, plugin_feature_t
*feature
,
250 bool reg
, void *data
)
252 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
255 lib
->credmgr
->add_set(lib
->credmgr
, &this->creds
->set
);
256 hydra
->attributes
->add_handler(hydra
->attributes
,
257 &this->attr
->handler
);
261 lib
->credmgr
->remove_set(lib
->credmgr
, &this->creds
->set
);
262 hydra
->attributes
->remove_handler(hydra
->attributes
,
263 &this->attr
->handler
);
266 this->service
->destroy(this->service
);
267 this->service
= NULL
;
274 * Initialize the charonservice object
276 static void charonservice_init(JNIEnv
*env
, jobject service
, jobject builder
)
278 private_charonservice_t
*this;
279 static plugin_feature_t features
[] = {
280 PLUGIN_CALLBACK(kernel_net_register
, kernel_android_net_create
),
281 PLUGIN_PROVIDE(CUSTOM
, "kernel-net"),
282 PLUGIN_CALLBACK(kernel_ipsec_register
, kernel_android_ipsec_create
),
283 PLUGIN_PROVIDE(CUSTOM
, "kernel-ipsec"),
284 PLUGIN_CALLBACK((plugin_feature_callback_t
)charonservice_register
, NULL
),
285 PLUGIN_PROVIDE(CUSTOM
, "Android backend"),
286 PLUGIN_DEPENDS(CUSTOM
, "libcharon"),
291 .update_status
= _update_status
,
292 .bypass_socket
= _bypass_socket
,
293 .get_trusted_certificates
= _get_trusted_certificates
,
294 .get_vpnservice_builder
= _get_vpnservice_builder
,
296 .attr
= android_attr_create(),
297 .creds
= android_creds_create(),
298 .builder
= vpnservice_builder_create(builder
),
299 .vpn_service
= (*env
)->NewGlobalRef(env
, service
),
301 charonservice
= &this->public;
303 lib
->plugins
->add_static_features(lib
->plugins
, "androidbridge", features
,
304 countof(features
), TRUE
);
306 lib
->settings
->set_int(lib
->settings
,
307 "charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL
);
308 lib
->settings
->set_int(lib
->settings
,
309 "charon.retransmit_tries", ANDROID_RETRASNMIT_TRIES
);
310 lib
->settings
->set_double(lib
->settings
,
311 "charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT
);
312 lib
->settings
->set_double(lib
->settings
,
313 "charon.retransmit_base", ANDROID_RETRANSMIT_BASE
);
314 lib
->settings
->set_bool(lib
->settings
,
315 "charon.close_ike_on_child_failure", TRUE
);
316 /* setting the source address breaks the VpnService.protect() function which
317 * uses SO_BINDTODEVICE internally. the addresses provided to the kernel as
318 * auxiliary data have precedence over this option causing a routing loop if
319 * the gateway is contained in the VPN routes. alternatively, providing an
320 * explicit device (in addition or instead of the source address) in the
321 * auxiliary data would also work, but we currently don't have that
323 lib
->settings
->set_bool(lib
->settings
,
324 "charon.plugins.socket-default.set_source", FALSE
);
328 * Deinitialize the charonservice object
330 static void charonservice_deinit(JNIEnv
*env
)
332 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
334 this->builder
->destroy(this->builder
);
335 this->creds
->destroy(this->creds
);
336 this->attr
->destroy(this->attr
);
337 (*env
)->DeleteGlobalRef(env
, this->vpn_service
);
339 charonservice
= NULL
;
343 * Handle SIGSEGV/SIGILL signals raised by threads
345 static void segv_handler(int signal
)
347 dbg_android(DBG_DMN
, 1, "thread %u received %d", thread_current_id(),
353 * Initialize charon and the libraries via JNI
355 JNI_METHOD(CharonVpnService
, initializeCharon
, void,
358 struct sigaction action
;
359 struct utsname utsname
;
361 /* logging for library during initialization, as we have no bus yet */
364 /* initialize library */
365 if (!library_init(NULL
))
371 if (!libhydra_init("charon"))
378 if (!libipsec_init())
386 if (!libcharon_init("charon"))
395 charonservice_init(env
, this, builder
);
397 if (uname(&utsname
) != 0)
399 memset(&utsname
, 0, sizeof(utsname
));
401 DBG1(DBG_DMN
, "Starting IKE charon daemon (strongSwan "VERSION
", %s %s, %s)",
402 utsname
.sysname
, utsname
.release
, utsname
.machine
);
404 if (!charon
->initialize(charon
, PLUGINS
))
407 charonservice_deinit(env
);
414 /* add handler for SEGV and ILL etc. */
415 action
.sa_handler
= segv_handler
;
417 sigemptyset(&action
.sa_mask
);
418 sigaction(SIGSEGV
, &action
, NULL
);
419 sigaction(SIGILL
, &action
, NULL
);
420 sigaction(SIGBUS
, &action
, NULL
);
421 action
.sa_handler
= SIG_IGN
;
422 sigaction(SIGPIPE
, &action
, NULL
);
424 /* start daemon (i.e. the threads in the thread-pool) */
425 charon
->start(charon
);
429 * Deinitialize charon and all libraries
431 JNI_METHOD(CharonVpnService
, deinitializeCharon
, void)
433 /* deinitialize charon before we destroy our own objects */
435 charonservice_deinit(env
);
444 JNI_METHOD(CharonVpnService
, initiate
, void,
445 jstring jlocal_address
, jstring jgateway
, jstring jusername
,
448 char *local_address
, *gateway
, *username
, *password
;
450 local_address
= androidjni_convert_jstring(env
, jlocal_address
);
451 gateway
= androidjni_convert_jstring(env
, jgateway
);
452 username
= androidjni_convert_jstring(env
, jusername
);
453 password
= androidjni_convert_jstring(env
, jpassword
);
455 initiate(local_address
, gateway
, username
, password
);