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>
24 #include "charonservice.h"
25 #include "android_jni.h"
26 #include "backend/android_attr.h"
27 #include "backend/android_creds.h"
28 #include "backend/android_private_key.h"
29 #include "backend/android_service.h"
30 #include "kernel/android_ipsec.h"
31 #include "kernel/android_net.h"
37 #include <threading/thread.h>
39 #define ANDROID_DEBUG_LEVEL 1
40 #define ANDROID_RETRASNMIT_TRIES 3
41 #define ANDROID_RETRANSMIT_TIMEOUT 2.0
42 #define ANDROID_RETRANSMIT_BASE 1.4
44 typedef struct private_charonservice_t private_charonservice_t
;
47 * private data of charonservice
49 struct private_charonservice_t
{
54 charonservice_t
public;
57 * android_attr instance
62 * android_creds instance
64 android_creds_t
*creds
;
67 * android_service instance
69 android_service_t
*service
;
72 * VpnService builder (accessed via JNI)
74 vpnservice_builder_t
*builder
;
77 * NetworkManager instance (accessed via JNI)
79 network_manager_t
*network_manager
;
82 * CharonVpnService reference
87 * Sockets that were bypassed and we keep track for
89 linked_list_t
*sockets
;
93 * Single instance of charonservice_t.
95 charonservice_t
*charonservice
;
98 * hook in library for debugging messages
100 extern void (*dbg
)(debug_t group
, level_t level
, char *fmt
, ...);
103 * Logging hook for library logs, using android specific logging
105 static void dbg_android(debug_t group
, level_t level
, char *fmt
, ...)
109 if (level
<= ANDROID_DEBUG_LEVEL
)
111 char sgroup
[16], buffer
[8192];
112 char *current
= buffer
, *next
;
114 snprintf(sgroup
, sizeof(sgroup
), "%N", debug_names
, group
);
116 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
119 { /* log each line separately */
120 next
= strchr(current
, '\n');
125 __android_log_print(ANDROID_LOG_INFO
, "charon", "00[%s] %s\n",
132 METHOD(charonservice_t
, update_status
, bool,
133 private_charonservice_t
*this, android_vpn_state_t code
)
137 bool success
= FALSE
;
139 androidjni_attach_thread(&env
);
141 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
142 "updateStatus", "(I)V");
147 (*env
)->CallVoidMethod(env
, this->vpn_service
, method_id
, (jint
)code
);
148 success
= !androidjni_exception_occurred(env
);
151 androidjni_exception_occurred(env
);
152 androidjni_detach_thread();
157 * Bypass a single socket
159 static bool bypass_single_socket(intptr_t fd
, private_charonservice_t
*this)
164 androidjni_attach_thread(&env
);
166 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
172 if (!(*env
)->CallBooleanMethod(env
, this->vpn_service
, method_id
, fd
))
174 DBG2(DBG_KNL
, "VpnService.protect() failed");
177 androidjni_detach_thread();
181 androidjni_exception_occurred(env
);
182 androidjni_detach_thread();
186 METHOD(charonservice_t
, bypass_socket
, bool,
187 private_charonservice_t
*this, int fd
, int family
)
191 this->sockets
->insert_last(this->sockets
, (void*)(intptr_t)fd
);
192 return bypass_single_socket((intptr_t)fd
, this);
194 this->sockets
->invoke_function(this->sockets
, (void*)bypass_single_socket
,
200 * Converts the given Java array of byte arrays (byte[][]) to a linked list
201 * of chunk_t objects.
203 static linked_list_t
*convert_array_of_byte_arrays(JNIEnv
*env
,
209 list
= linked_list_create();
210 for (i
= 0; i
< (*env
)->GetArrayLength(env
, jarray
); ++i
)
213 jbyteArray jbytearray
;
215 chunk
= malloc_thing(chunk_t
);
216 list
->insert_last(list
, chunk
);
218 jbytearray
= (*env
)->GetObjectArrayElement(env
, jarray
, i
);
219 *chunk
= chunk_alloc((*env
)->GetArrayLength(env
, jbytearray
));
220 (*env
)->GetByteArrayRegion(env
, jbytearray
, 0, chunk
->len
, chunk
->ptr
);
221 (*env
)->DeleteLocalRef(env
, jbytearray
);
226 METHOD(charonservice_t
, get_trusted_certificates
, linked_list_t
*,
227 private_charonservice_t
*this)
234 androidjni_attach_thread(&env
);
236 method_id
= (*env
)->GetMethodID(env
,
237 android_charonvpnservice_class
,
238 "getTrustedCertificates", "(Ljava/lang/String;)[[B");
243 jcerts
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
, NULL
);
244 if (!jcerts
|| androidjni_exception_occurred(env
))
248 list
= convert_array_of_byte_arrays(env
, jcerts
);
249 androidjni_detach_thread();
253 androidjni_exception_occurred(env
);
254 androidjni_detach_thread();
258 METHOD(charonservice_t
, get_user_certificate
, linked_list_t
*,
259 private_charonservice_t
*this)
263 jobjectArray jencodings
;
266 androidjni_attach_thread(&env
);
268 method_id
= (*env
)->GetMethodID(env
,
269 android_charonvpnservice_class
,
270 "getUserCertificate", "()[[B");
275 jencodings
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
);
276 if (!jencodings
|| androidjni_exception_occurred(env
))
280 list
= convert_array_of_byte_arrays(env
, jencodings
);
281 androidjni_detach_thread();
285 androidjni_exception_occurred(env
);
286 androidjni_detach_thread();
290 METHOD(charonservice_t
, get_user_key
, private_key_t
*,
291 private_charonservice_t
*this, public_key_t
*pubkey
)
298 androidjni_attach_thread(&env
);
300 method_id
= (*env
)->GetMethodID(env
,
301 android_charonvpnservice_class
,
302 "getUserKey", "()Ljava/security/PrivateKey;");
307 jkey
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
);
308 if (!jkey
|| androidjni_exception_occurred(env
))
312 key
= android_private_key_create(jkey
, pubkey
);
313 androidjni_detach_thread();
318 androidjni_exception_occurred(env
);
319 androidjni_detach_thread();
323 METHOD(charonservice_t
, get_vpnservice_builder
, vpnservice_builder_t
*,
324 private_charonservice_t
*this)
326 return this->builder
;
329 METHOD(charonservice_t
, get_network_manager
, network_manager_t
*,
330 private_charonservice_t
*this)
332 return this->network_manager
;
336 * Initiate a new connection
338 * @param gateway gateway address (gets owned)
339 * @param username username (gets owned)
340 * @param password password (gets owned)
342 static void initiate(char *type
, char *gateway
, char *username
, char *password
)
344 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
346 this->creds
->clear(this->creds
);
347 DESTROY_IF(this->service
);
348 this->service
= android_service_create(this->creds
, type
, gateway
,
353 * Initialize/deinitialize Android backend
355 static bool charonservice_register(void *plugin
, plugin_feature_t
*feature
,
356 bool reg
, void *data
)
358 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
361 lib
->credmgr
->add_set(lib
->credmgr
, &this->creds
->set
);
362 hydra
->attributes
->add_handler(hydra
->attributes
,
363 &this->attr
->handler
);
367 lib
->credmgr
->remove_set(lib
->credmgr
, &this->creds
->set
);
368 hydra
->attributes
->remove_handler(hydra
->attributes
,
369 &this->attr
->handler
);
372 this->service
->destroy(this->service
);
373 this->service
= NULL
;
380 * Set strongswan.conf options
382 static void set_options(char *logfile
)
384 lib
->settings
->set_int(lib
->settings
,
385 "charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL
);
386 /* setup file logger */
387 lib
->settings
->set_str(lib
->settings
,
388 "charon.filelog.%s.time_format", "%b %e %T", logfile
);
389 lib
->settings
->set_bool(lib
->settings
,
390 "charon.filelog.%s.append", FALSE
, logfile
);
391 lib
->settings
->set_bool(lib
->settings
,
392 "charon.filelog.%s.flush_line", TRUE
, logfile
);
393 lib
->settings
->set_int(lib
->settings
,
394 "charon.filelog.%s.default", ANDROID_DEBUG_LEVEL
, logfile
);
396 lib
->settings
->set_int(lib
->settings
,
397 "charon.retransmit_tries", ANDROID_RETRASNMIT_TRIES
);
398 lib
->settings
->set_double(lib
->settings
,
399 "charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT
);
400 lib
->settings
->set_double(lib
->settings
,
401 "charon.retransmit_base", ANDROID_RETRANSMIT_BASE
);
402 lib
->settings
->set_bool(lib
->settings
,
403 "charon.close_ike_on_child_failure", TRUE
);
404 /* setting the source address breaks the VpnService.protect() function which
405 * uses SO_BINDTODEVICE internally. the addresses provided to the kernel as
406 * auxiliary data have precedence over this option causing a routing loop if
407 * the gateway is contained in the VPN routes. alternatively, providing an
408 * explicit device (in addition or instead of the source address) in the
409 * auxiliary data would also work, but we currently don't have that
411 lib
->settings
->set_bool(lib
->settings
,
412 "charon.plugins.socket-default.set_source", FALSE
);
413 /* don't install virtual IPs via kernel-netlink */
414 lib
->settings
->set_bool(lib
->settings
,
415 "charon.install_virtual_ip", FALSE
);
416 /* ignore tun devices (it's mostly tun0 but it may already be taken, ignore
417 * some others too) */
418 lib
->settings
->set_str(lib
->settings
,
419 "charon.interfaces_ignore", "tun0, tun1, tun2, tun3, tun4");
423 * Initialize the charonservice object
425 static void charonservice_init(JNIEnv
*env
, jobject service
, jobject builder
)
427 private_charonservice_t
*this;
428 static plugin_feature_t features
[] = {
429 PLUGIN_CALLBACK(kernel_ipsec_register
, kernel_android_ipsec_create
),
430 PLUGIN_PROVIDE(CUSTOM
, "kernel-ipsec"),
431 PLUGIN_CALLBACK((plugin_feature_callback_t
)charonservice_register
, NULL
),
432 PLUGIN_PROVIDE(CUSTOM
, "Android backend"),
433 PLUGIN_DEPENDS(CUSTOM
, "libcharon"),
438 .update_status
= _update_status
,
439 .bypass_socket
= _bypass_socket
,
440 .get_trusted_certificates
= _get_trusted_certificates
,
441 .get_user_certificate
= _get_user_certificate
,
442 .get_user_key
= _get_user_key
,
443 .get_vpnservice_builder
= _get_vpnservice_builder
,
444 .get_network_manager
= _get_network_manager
,
446 .attr
= android_attr_create(),
447 .creds
= android_creds_create(),
448 .builder
= vpnservice_builder_create(builder
),
449 .network_manager
= network_manager_create(service
),
450 .sockets
= linked_list_create(),
451 .vpn_service
= (*env
)->NewGlobalRef(env
, service
),
453 charonservice
= &this->public;
455 lib
->plugins
->add_static_features(lib
->plugins
, "androidbridge", features
,
456 countof(features
), TRUE
);
460 * Deinitialize the charonservice object
462 static void charonservice_deinit(JNIEnv
*env
)
464 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
466 this->network_manager
->destroy(this->network_manager
);
467 this->sockets
->destroy(this->sockets
);
468 this->builder
->destroy(this->builder
);
469 this->creds
->destroy(this->creds
);
470 this->attr
->destroy(this->attr
);
471 (*env
)->DeleteGlobalRef(env
, this->vpn_service
);
473 charonservice
= NULL
;
477 * Handle SIGSEGV/SIGILL signals raised by threads
479 static void segv_handler(int signal
)
481 dbg_android(DBG_DMN
, 1, "thread %u received %d", thread_current_id(),
487 * Initialize charon and the libraries via JNI
489 JNI_METHOD(CharonVpnService
, initializeCharon
, void,
490 jobject builder
, jstring jlogfile
)
492 struct sigaction action
;
493 struct utsname utsname
;
496 /* logging for library during initialization, as we have no bus yet */
499 /* initialize library */
500 if (!library_init(NULL
))
506 /* set options before initializing other libraries that might read them */
507 logfile
= androidjni_convert_jstring(env
, jlogfile
);
508 set_options(logfile
);
511 if (!libhydra_init("charon"))
518 if (!libipsec_init())
526 if (!libcharon_init("charon"))
535 charon
->load_loggers(charon
, NULL
, FALSE
);
537 charonservice_init(env
, this, builder
);
539 if (uname(&utsname
) != 0)
541 memset(&utsname
, 0, sizeof(utsname
));
543 DBG1(DBG_DMN
, "Starting IKE charon daemon (strongSwan "VERSION
", %s %s, %s)",
544 utsname
.sysname
, utsname
.release
, utsname
.machine
);
546 if (!charon
->initialize(charon
, PLUGINS
))
549 charonservice_deinit(env
);
556 /* add handler for SEGV and ILL etc. */
557 action
.sa_handler
= segv_handler
;
559 sigemptyset(&action
.sa_mask
);
560 sigaction(SIGSEGV
, &action
, NULL
);
561 sigaction(SIGILL
, &action
, NULL
);
562 sigaction(SIGBUS
, &action
, NULL
);
563 action
.sa_handler
= SIG_IGN
;
564 sigaction(SIGPIPE
, &action
, NULL
);
566 /* start daemon (i.e. the threads in the thread-pool) */
567 charon
->start(charon
);
571 * Deinitialize charon and all libraries
573 JNI_METHOD(CharonVpnService
, deinitializeCharon
, void)
575 /* deinitialize charon before we destroy our own objects */
577 charonservice_deinit(env
);
586 JNI_METHOD(CharonVpnService
, initiate
, void,
587 jstring jtype
, jstring jgateway
, jstring jusername
, jstring jpassword
)
589 char *type
, *gateway
, *username
, *password
;
591 type
= androidjni_convert_jstring(env
, jtype
);
592 gateway
= androidjni_convert_jstring(env
, jgateway
);
593 username
= androidjni_convert_jstring(env
, jusername
);
594 password
= androidjni_convert_jstring(env
, jpassword
);
596 initiate(type
, gateway
, username
, password
);