2 * Copyright (C) 2012-2017 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR 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_fetcher.h"
29 #include "backend/android_private_key.h"
30 #include "backend/android_service.h"
31 #include "kernel/android_ipsec.h"
32 #include "kernel/android_net.h"
35 #include "byod/imc_android.h"
41 #include <threading/thread.h>
43 #define ANDROID_DEBUG_LEVEL 1
44 #define ANDROID_RETRASNMIT_TRIES 3
45 #define ANDROID_RETRANSMIT_TIMEOUT 2.0
46 #define ANDROID_RETRANSMIT_BASE 1.4
47 #define ANDROID_KEEPALIVE_INTERVAL 45
49 typedef struct private_charonservice_t private_charonservice_t
;
52 * private data of charonservice
54 struct private_charonservice_t
{
59 charonservice_t
public;
62 * android_attr instance
67 * android_creds instance
69 android_creds_t
*creds
;
72 * android_service instance
74 android_service_t
*service
;
77 * VpnService builder (accessed via JNI)
79 vpnservice_builder_t
*builder
;
82 * NetworkManager instance (accessed via JNI)
84 network_manager_t
*network_manager
;
87 * CharonVpnService reference
92 * Sockets that were bypassed and we keep track for
94 linked_list_t
*sockets
;
98 * Single instance of charonservice_t.
100 charonservice_t
*charonservice
;
103 * hook in library for debugging messages
105 extern void (*dbg
)(debug_t group
, level_t level
, char *fmt
, ...);
108 * Logging hook for library logs, using android specific logging
110 static void dbg_android(debug_t group
, level_t level
, char *fmt
, ...)
114 if (level
<= ANDROID_DEBUG_LEVEL
)
116 char sgroup
[16], buffer
[8192];
117 char *current
= buffer
, *next
;
119 snprintf(sgroup
, sizeof(sgroup
), "%N", debug_names
, group
);
121 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
124 { /* log each line separately */
125 next
= strchr(current
, '\n');
130 __android_log_print(ANDROID_LOG_INFO
, "charon", "00[%s] %s\n",
137 METHOD(charonservice_t
, update_status
, bool,
138 private_charonservice_t
*this, android_vpn_state_t code
)
142 bool success
= FALSE
;
144 androidjni_attach_thread(&env
);
146 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
147 "updateStatus", "(I)V");
152 (*env
)->CallVoidMethod(env
, this->vpn_service
, method_id
, (jint
)code
);
153 success
= !androidjni_exception_occurred(env
);
156 androidjni_exception_occurred(env
);
157 androidjni_detach_thread();
161 METHOD(charonservice_t
, update_imc_state
, bool,
162 private_charonservice_t
*this, android_imc_state_t state
)
166 bool success
= FALSE
;
168 androidjni_attach_thread(&env
);
170 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
171 "updateImcState", "(I)V");
176 (*env
)->CallVoidMethod(env
, this->vpn_service
, method_id
, (jint
)state
);
177 success
= !androidjni_exception_occurred(env
);
180 androidjni_exception_occurred(env
);
181 androidjni_detach_thread();
185 METHOD(charonservice_t
, add_remediation_instr
, bool,
186 private_charonservice_t
*this, char *instr
)
191 bool success
= FALSE
;
193 androidjni_attach_thread(&env
);
195 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
196 "addRemediationInstruction",
197 "(Ljava/lang/String;)V");
202 jinstr
= (*env
)->NewStringUTF(env
, instr
);
207 (*env
)->CallVoidMethod(env
, this->vpn_service
, method_id
, jinstr
);
208 success
= !androidjni_exception_occurred(env
);
211 androidjni_exception_occurred(env
);
212 androidjni_detach_thread();
217 * Bypass a single socket
219 static bool bypass_single_socket(private_charonservice_t
*this, int fd
)
224 androidjni_attach_thread(&env
);
226 method_id
= (*env
)->GetMethodID(env
, android_charonvpnservice_class
,
232 if (!(*env
)->CallBooleanMethod(env
, this->vpn_service
, method_id
, fd
))
234 DBG2(DBG_KNL
, "VpnService.protect() failed");
237 androidjni_detach_thread();
241 androidjni_exception_occurred(env
);
242 androidjni_detach_thread();
246 CALLBACK(bypass_single_socket_cb
, void,
247 intptr_t fd
, va_list args
)
249 private_charonservice_t
*this;
251 VA_ARGS_VGET(args
, this);
252 bypass_single_socket(this, fd
);
255 METHOD(charonservice_t
, bypass_socket
, bool,
256 private_charonservice_t
*this, int fd
, int family
)
260 this->sockets
->insert_last(this->sockets
, (void*)(intptr_t)fd
);
261 return bypass_single_socket(this, fd
);
263 this->sockets
->invoke_function(this->sockets
, bypass_single_socket_cb
, this);
268 * Converts the given Java array of byte arrays (byte[][]) to a linked list
269 * of chunk_t objects.
271 static linked_list_t
*convert_array_of_byte_arrays(JNIEnv
*env
,
277 list
= linked_list_create();
278 for (i
= 0; i
< (*env
)->GetArrayLength(env
, jarray
); ++i
)
281 jbyteArray jbytearray
;
283 chunk
= malloc_thing(chunk_t
);
284 list
->insert_last(list
, chunk
);
286 jbytearray
= (*env
)->GetObjectArrayElement(env
, jarray
, i
);
287 *chunk
= chunk_alloc((*env
)->GetArrayLength(env
, jbytearray
));
288 (*env
)->GetByteArrayRegion(env
, jbytearray
, 0, chunk
->len
, chunk
->ptr
);
289 (*env
)->DeleteLocalRef(env
, jbytearray
);
294 METHOD(charonservice_t
, get_trusted_certificates
, linked_list_t
*,
295 private_charonservice_t
*this)
302 androidjni_attach_thread(&env
);
304 method_id
= (*env
)->GetMethodID(env
,
305 android_charonvpnservice_class
,
306 "getTrustedCertificates", "()[[B");
311 jcerts
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
);
312 if (!jcerts
|| androidjni_exception_occurred(env
))
316 list
= convert_array_of_byte_arrays(env
, jcerts
);
317 androidjni_detach_thread();
321 androidjni_exception_occurred(env
);
322 androidjni_detach_thread();
326 METHOD(charonservice_t
, get_user_certificate
, linked_list_t
*,
327 private_charonservice_t
*this)
331 jobjectArray jencodings
;
334 androidjni_attach_thread(&env
);
336 method_id
= (*env
)->GetMethodID(env
,
337 android_charonvpnservice_class
,
338 "getUserCertificate", "()[[B");
343 jencodings
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
);
344 if (!jencodings
|| androidjni_exception_occurred(env
))
348 list
= convert_array_of_byte_arrays(env
, jencodings
);
349 androidjni_detach_thread();
353 androidjni_exception_occurred(env
);
354 androidjni_detach_thread();
358 METHOD(charonservice_t
, get_user_key
, private_key_t
*,
359 private_charonservice_t
*this, public_key_t
*pubkey
)
366 androidjni_attach_thread(&env
);
368 method_id
= (*env
)->GetMethodID(env
,
369 android_charonvpnservice_class
,
370 "getUserKey", "()Ljava/security/PrivateKey;");
375 jkey
= (*env
)->CallObjectMethod(env
, this->vpn_service
, method_id
);
376 if (!jkey
|| androidjni_exception_occurred(env
))
380 key
= android_private_key_create(jkey
, pubkey
);
381 androidjni_detach_thread();
386 androidjni_exception_occurred(env
);
387 androidjni_detach_thread();
391 METHOD(charonservice_t
, get_vpnservice_builder
, vpnservice_builder_t
*,
392 private_charonservice_t
*this)
394 return this->builder
;
397 METHOD(charonservice_t
, get_network_manager
, network_manager_t
*,
398 private_charonservice_t
*this)
400 return this->network_manager
;
404 * Initiate a new connection
406 * @param settings configuration settings (gets owned)
408 static void initiate(settings_t
*settings
)
410 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
412 lib
->settings
->set_str(lib
->settings
,
413 "charon.plugins.tnc-imc.preferred_language",
414 settings
->get_str(settings
, "global.language", "en"));
415 /* this is actually the size of the complete IKE/IP packet, so if the MTU
416 * for the TUN devices has to be reduced to pass traffic the IKE packets
417 * will be a bit smaller than necessary as there is no IPsec overhead like
418 * for the tunneled traffic (but compensating that seems like overkill) */
419 lib
->settings
->set_int(lib
->settings
,
420 "charon.fragment_size",
421 settings
->get_int(settings
, "global.mtu",
422 ANDROID_DEFAULT_MTU
));
424 this->creds
->clear(this->creds
);
425 DESTROY_IF(this->service
);
426 this->service
= android_service_create(this->creds
, settings
);
430 * Initialize/deinitialize Android backend
432 static bool charonservice_register(plugin_t
*plugin
, plugin_feature_t
*feature
,
433 bool reg
, void *data
)
435 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
438 lib
->credmgr
->add_set(lib
->credmgr
, &this->creds
->set
);
439 charon
->attributes
->add_handler(charon
->attributes
,
440 &this->attr
->handler
);
444 lib
->credmgr
->remove_set(lib
->credmgr
, &this->creds
->set
);
445 charon
->attributes
->remove_handler(charon
->attributes
,
446 &this->attr
->handler
);
449 this->service
->destroy(this->service
);
450 this->service
= NULL
;
457 * Set strongswan.conf options
459 static void set_options(char *logfile
)
461 lib
->settings
->set_int(lib
->settings
,
462 "charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL
);
463 /* setup file logger */
464 lib
->settings
->set_str(lib
->settings
,
465 "charon.filelog.%s.time_format", "%b %e %T", logfile
);
466 lib
->settings
->set_bool(lib
->settings
,
467 "charon.filelog.%s.append", FALSE
, logfile
);
468 lib
->settings
->set_bool(lib
->settings
,
469 "charon.filelog.%s.flush_line", TRUE
, logfile
);
470 lib
->settings
->set_int(lib
->settings
,
471 "charon.filelog.%s.default", ANDROID_DEBUG_LEVEL
, logfile
);
473 lib
->settings
->set_int(lib
->settings
,
474 "charon.retransmit_tries", ANDROID_RETRASNMIT_TRIES
);
475 lib
->settings
->set_double(lib
->settings
,
476 "charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT
);
477 lib
->settings
->set_double(lib
->settings
,
478 "charon.retransmit_base", ANDROID_RETRANSMIT_BASE
);
479 /* increase NAT-T keepalive interval a bit to save battery power */
480 lib
->settings
->set_time(lib
->settings
,
481 "charon.keep_alive", ANDROID_KEEPALIVE_INTERVAL
);
482 lib
->settings
->set_bool(lib
->settings
,
483 "charon.initiator_only", TRUE
);
484 lib
->settings
->set_bool(lib
->settings
,
485 "charon.close_ike_on_child_failure", TRUE
);
486 /* setting the source address breaks the VpnService.protect() function which
487 * uses SO_BINDTODEVICE internally. the addresses provided to the kernel as
488 * auxiliary data have precedence over this option causing a routing loop if
489 * the gateway is contained in the VPN routes. alternatively, providing an
490 * explicit device (in addition or instead of the source address) in the
491 * auxiliary data would also work, but we currently don't have that
493 lib
->settings
->set_bool(lib
->settings
,
494 "charon.plugins.socket-default.set_source", FALSE
);
495 /* the Linux kernel does currently not support UDP encaspulation for IPv6
496 * so lets disable IPv6 for now to avoid issues with dual-stack gateways */
497 lib
->settings
->set_bool(lib
->settings
,
498 "charon.plugins.socket-default.use_ipv6", FALSE
);
501 lib
->settings
->set_str(lib
->settings
,
502 "charon.plugins.eap-tnc.protocol", "tnccs-2.0");
503 lib
->settings
->set_int(lib
->settings
,
504 "charon.plugins.eap-ttls.max_message_count", 0);
505 lib
->settings
->set_bool(lib
->settings
,
506 "android.imc.send_os_info", TRUE
);
507 lib
->settings
->set_str(lib
->settings
,
508 "libtnccs.tnc_config", "");
513 * Initialize the charonservice object
515 static void charonservice_init(JNIEnv
*env
, jobject service
, jobject builder
,
518 private_charonservice_t
*this;
519 static plugin_feature_t features
[] = {
520 PLUGIN_CALLBACK(kernel_ipsec_register
, kernel_android_ipsec_create
),
521 PLUGIN_PROVIDE(CUSTOM
, "kernel-ipsec"),
522 PLUGIN_CALLBACK(kernel_net_register
, kernel_android_net_create
),
523 PLUGIN_PROVIDE(CUSTOM
, "kernel-net"),
524 PLUGIN_CALLBACK(charonservice_register
, NULL
),
525 PLUGIN_PROVIDE(CUSTOM
, "android-backend"),
526 PLUGIN_DEPENDS(CUSTOM
, "libcharon"),
527 PLUGIN_REGISTER(FETCHER
, android_fetcher_create
),
528 PLUGIN_PROVIDE(FETCHER
, "http://"),
529 PLUGIN_PROVIDE(FETCHER
, "https://"),
534 .update_status
= _update_status
,
535 .update_imc_state
= _update_imc_state
,
536 .add_remediation_instr
= _add_remediation_instr
,
537 .bypass_socket
= _bypass_socket
,
538 .get_trusted_certificates
= _get_trusted_certificates
,
539 .get_user_certificate
= _get_user_certificate
,
540 .get_user_key
= _get_user_key
,
541 .get_vpnservice_builder
= _get_vpnservice_builder
,
542 .get_network_manager
= _get_network_manager
,
544 .attr
= android_attr_create(),
545 .creds
= android_creds_create(),
546 .builder
= vpnservice_builder_create(builder
),
547 .network_manager
= network_manager_create(service
),
548 .sockets
= linked_list_create(),
549 .vpn_service
= (*env
)->NewGlobalRef(env
, service
),
551 charonservice
= &this->public;
553 lib
->plugins
->add_static_features(lib
->plugins
, "androidbridge", features
,
554 countof(features
), TRUE
, NULL
, NULL
);
559 plugin_feature_t byod_features
[] = {
560 PLUGIN_CALLBACK(imc_android_register
, this->vpn_service
),
561 PLUGIN_PROVIDE(CUSTOM
, "android-imc"),
562 PLUGIN_DEPENDS(CUSTOM
, "android-backend"),
563 PLUGIN_DEPENDS(CUSTOM
, "imc-manager"),
566 lib
->plugins
->add_static_features(lib
->plugins
, "android-byod",
567 byod_features
, countof(byod_features
), TRUE
, NULL
, NULL
);
573 * Deinitialize the charonservice object
575 static void charonservice_deinit(JNIEnv
*env
)
577 private_charonservice_t
*this = (private_charonservice_t
*)charonservice
;
579 this->network_manager
->destroy(this->network_manager
);
580 this->sockets
->destroy(this->sockets
);
581 this->builder
->destroy(this->builder
);
582 this->creds
->destroy(this->creds
);
583 this->attr
->destroy(this->attr
);
584 (*env
)->DeleteGlobalRef(env
, this->vpn_service
);
586 charonservice
= NULL
;
590 * Handle SIGSEGV/SIGILL signals raised by threads
592 static void segv_handler(int signal
)
594 dbg_android(DBG_DMN
, 1, "thread %u received %d", thread_current_id(),
600 * Initialize charon and the libraries via JNI
602 JNI_METHOD(CharonVpnService
, initializeCharon
, jboolean
,
603 jobject builder
, jstring jlogfile
, jboolean byod
)
605 struct sigaction action
;
606 struct utsname utsname
;
607 char *logfile
, *plugins
;
609 /* logging for library during initialization, as we have no bus yet */
612 /* initialize library */
613 if (!library_init(NULL
, "charon"))
619 /* set options before initializing other libraries that might read them */
620 logfile
= androidjni_convert_jstring(env
, jlogfile
);
621 set_options(logfile
);
624 if (!libipsec_init())
631 if (!libcharon_init())
639 charon
->load_loggers(charon
);
641 charonservice_init(env
, this, builder
, byod
);
643 if (uname(&utsname
) != 0)
645 memset(&utsname
, 0, sizeof(utsname
));
647 DBG1(DBG_DMN
, "Starting IKE charon daemon (strongSwan "VERSION
", %s %s, %s)",
648 utsname
.sysname
, utsname
.release
, utsname
.machine
);
653 plugins
= PLUGINS
" " PLUGINS_BYOD
;
661 if (!charon
->initialize(charon
, plugins
))
664 charonservice_deinit(env
);
669 lib
->plugins
->status(lib
->plugins
, LEVEL_CTRL
);
671 /* add handler for SEGV and ILL etc. */
672 action
.sa_handler
= segv_handler
;
674 sigemptyset(&action
.sa_mask
);
675 sigaction(SIGSEGV
, &action
, NULL
);
676 sigaction(SIGILL
, &action
, NULL
);
677 sigaction(SIGBUS
, &action
, NULL
);
678 action
.sa_handler
= SIG_IGN
;
679 sigaction(SIGPIPE
, &action
, NULL
);
681 /* start daemon (i.e. the threads in the thread-pool) */
682 charon
->start(charon
);
687 * Deinitialize charon and all libraries
689 JNI_METHOD(CharonVpnService
, deinitializeCharon
, void)
691 /* deinitialize charon before we destroy our own objects */
693 charonservice_deinit(env
);
701 JNI_METHOD(CharonVpnService
, initiate
, void,
704 settings_t
*settings
;
707 config
= androidjni_convert_jstring(env
, jconfig
);
708 settings
= settings_create_string(config
);