3bed6eb4d06caba06e53cf7f0be9afd7b5b3a386
[strongswan.git] / src / frontends / android / app / src / main / jni / libandroidbridge / charonservice.c
1 /*
2 * Copyright (C) 2012-2015 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
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>.
11 *
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
15 * for more details.
16 */
17
18 #include <signal.h>
19 #include <string.h>
20 #include <sys/utsname.h>
21 #include <android/log.h>
22 #include <errno.h>
23
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"
32
33 #ifdef USE_BYOD
34 #include "byod/imc_android.h"
35 #endif
36
37 #include <daemon.h>
38 #include <ipsec.h>
39 #include <library.h>
40 #include <threading/thread.h>
41
42 #define ANDROID_DEBUG_LEVEL 1
43 #define ANDROID_RETRASNMIT_TRIES 3
44 #define ANDROID_RETRANSMIT_TIMEOUT 2.0
45 #define ANDROID_RETRANSMIT_BASE 1.4
46
47 typedef struct private_charonservice_t private_charonservice_t;
48
49 /**
50 * private data of charonservice
51 */
52 struct private_charonservice_t {
53
54 /**
55 * public interface
56 */
57 charonservice_t public;
58
59 /**
60 * android_attr instance
61 */
62 android_attr_t *attr;
63
64 /**
65 * android_creds instance
66 */
67 android_creds_t *creds;
68
69 /**
70 * android_service instance
71 */
72 android_service_t *service;
73
74 /**
75 * VpnService builder (accessed via JNI)
76 */
77 vpnservice_builder_t *builder;
78
79 /**
80 * NetworkManager instance (accessed via JNI)
81 */
82 network_manager_t *network_manager;
83
84 /**
85 * CharonVpnService reference
86 */
87 jobject vpn_service;
88
89 /**
90 * Sockets that were bypassed and we keep track for
91 */
92 linked_list_t *sockets;
93 };
94
95 /**
96 * Single instance of charonservice_t.
97 */
98 charonservice_t *charonservice;
99
100 /**
101 * hook in library for debugging messages
102 */
103 extern void (*dbg)(debug_t group, level_t level, char *fmt, ...);
104
105 /**
106 * Logging hook for library logs, using android specific logging
107 */
108 static void dbg_android(debug_t group, level_t level, char *fmt, ...)
109 {
110 va_list args;
111
112 if (level <= ANDROID_DEBUG_LEVEL)
113 {
114 char sgroup[16], buffer[8192];
115 char *current = buffer, *next;
116
117 snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group);
118 va_start(args, fmt);
119 vsnprintf(buffer, sizeof(buffer), fmt, args);
120 va_end(args);
121 while (current)
122 { /* log each line separately */
123 next = strchr(current, '\n');
124 if (next)
125 {
126 *(next++) = '\0';
127 }
128 __android_log_print(ANDROID_LOG_INFO, "charon", "00[%s] %s\n",
129 sgroup, current);
130 current = next;
131 }
132 }
133 }
134
135 METHOD(charonservice_t, update_status, bool,
136 private_charonservice_t *this, android_vpn_state_t code)
137 {
138 JNIEnv *env;
139 jmethodID method_id;
140 bool success = FALSE;
141
142 androidjni_attach_thread(&env);
143
144 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
145 "updateStatus", "(I)V");
146 if (!method_id)
147 {
148 goto failed;
149 }
150 (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)code);
151 success = !androidjni_exception_occurred(env);
152
153 failed:
154 androidjni_exception_occurred(env);
155 androidjni_detach_thread();
156 return success;
157 }
158
159 METHOD(charonservice_t, update_imc_state, bool,
160 private_charonservice_t *this, android_imc_state_t state)
161 {
162 JNIEnv *env;
163 jmethodID method_id;
164 bool success = FALSE;
165
166 androidjni_attach_thread(&env);
167
168 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
169 "updateImcState", "(I)V");
170 if (!method_id)
171 {
172 goto failed;
173 }
174 (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)state);
175 success = !androidjni_exception_occurred(env);
176
177 failed:
178 androidjni_exception_occurred(env);
179 androidjni_detach_thread();
180 return success;
181 }
182
183 METHOD(charonservice_t, add_remediation_instr, bool,
184 private_charonservice_t *this, char *instr)
185 {
186 JNIEnv *env;
187 jmethodID method_id;
188 jstring jinstr;
189 bool success = FALSE;
190
191 androidjni_attach_thread(&env);
192
193 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
194 "addRemediationInstruction",
195 "(Ljava/lang/String;)V");
196 if (!method_id)
197 {
198 goto failed;
199 }
200 jinstr = (*env)->NewStringUTF(env, instr);
201 if (!jinstr)
202 {
203 goto failed;
204 }
205 (*env)->CallVoidMethod(env, this->vpn_service, method_id, jinstr);
206 success = !androidjni_exception_occurred(env);
207
208 failed:
209 androidjni_exception_occurred(env);
210 androidjni_detach_thread();
211 return success;
212 }
213
214 /**
215 * Bypass a single socket
216 */
217 static bool bypass_single_socket(intptr_t fd, private_charonservice_t *this)
218 {
219 JNIEnv *env;
220 jmethodID method_id;
221
222 androidjni_attach_thread(&env);
223
224 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
225 "protect", "(I)Z");
226 if (!method_id)
227 {
228 goto failed;
229 }
230 if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
231 {
232 DBG2(DBG_KNL, "VpnService.protect() failed");
233 goto failed;
234 }
235 androidjni_detach_thread();
236 return TRUE;
237
238 failed:
239 androidjni_exception_occurred(env);
240 androidjni_detach_thread();
241 return FALSE;
242 }
243
244 METHOD(charonservice_t, bypass_socket, bool,
245 private_charonservice_t *this, int fd, int family)
246 {
247 if (fd >= 0)
248 {
249 this->sockets->insert_last(this->sockets, (void*)(intptr_t)fd);
250 return bypass_single_socket((intptr_t)fd, this);
251 }
252 this->sockets->invoke_function(this->sockets, (void*)bypass_single_socket,
253 this);
254 return TRUE;
255 }
256
257 /**
258 * Converts the given Java array of byte arrays (byte[][]) to a linked list
259 * of chunk_t objects.
260 */
261 static linked_list_t *convert_array_of_byte_arrays(JNIEnv *env,
262 jobjectArray jarray)
263 {
264 linked_list_t *list;
265 jsize i;
266
267 list = linked_list_create();
268 for (i = 0; i < (*env)->GetArrayLength(env, jarray); ++i)
269 {
270 chunk_t *chunk;
271 jbyteArray jbytearray;
272
273 chunk = malloc_thing(chunk_t);
274 list->insert_last(list, chunk);
275
276 jbytearray = (*env)->GetObjectArrayElement(env, jarray, i);
277 *chunk = chunk_alloc((*env)->GetArrayLength(env, jbytearray));
278 (*env)->GetByteArrayRegion(env, jbytearray, 0, chunk->len, chunk->ptr);
279 (*env)->DeleteLocalRef(env, jbytearray);
280 }
281 return list;
282 }
283
284 METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
285 private_charonservice_t *this)
286 {
287 JNIEnv *env;
288 jmethodID method_id;
289 jobjectArray jcerts;
290 linked_list_t *list;
291
292 androidjni_attach_thread(&env);
293
294 method_id = (*env)->GetMethodID(env,
295 android_charonvpnservice_class,
296 "getTrustedCertificates", "()[[B");
297 if (!method_id)
298 {
299 goto failed;
300 }
301 jcerts = (*env)->CallObjectMethod(env, this->vpn_service, method_id);
302 if (!jcerts || androidjni_exception_occurred(env))
303 {
304 goto failed;
305 }
306 list = convert_array_of_byte_arrays(env, jcerts);
307 androidjni_detach_thread();
308 return list;
309
310 failed:
311 androidjni_exception_occurred(env);
312 androidjni_detach_thread();
313 return NULL;
314 }
315
316 METHOD(charonservice_t, get_user_certificate, linked_list_t*,
317 private_charonservice_t *this)
318 {
319 JNIEnv *env;
320 jmethodID method_id;
321 jobjectArray jencodings;
322 linked_list_t *list;
323
324 androidjni_attach_thread(&env);
325
326 method_id = (*env)->GetMethodID(env,
327 android_charonvpnservice_class,
328 "getUserCertificate", "()[[B");
329 if (!method_id)
330 {
331 goto failed;
332 }
333 jencodings = (*env)->CallObjectMethod(env, this->vpn_service, method_id);
334 if (!jencodings || androidjni_exception_occurred(env))
335 {
336 goto failed;
337 }
338 list = convert_array_of_byte_arrays(env, jencodings);
339 androidjni_detach_thread();
340 return list;
341
342 failed:
343 androidjni_exception_occurred(env);
344 androidjni_detach_thread();
345 return NULL;
346 }
347
348 METHOD(charonservice_t, get_user_key, private_key_t*,
349 private_charonservice_t *this, public_key_t *pubkey)
350 {
351 JNIEnv *env;
352 jmethodID method_id;
353 private_key_t *key;
354 jobject jkey;
355
356 androidjni_attach_thread(&env);
357
358 method_id = (*env)->GetMethodID(env,
359 android_charonvpnservice_class,
360 "getUserKey", "()Ljava/security/PrivateKey;");
361 if (!method_id)
362 {
363 goto failed;
364 }
365 jkey = (*env)->CallObjectMethod(env, this->vpn_service, method_id);
366 if (!jkey || androidjni_exception_occurred(env))
367 {
368 goto failed;
369 }
370 key = android_private_key_create(jkey, pubkey);
371 androidjni_detach_thread();
372 return key;
373
374 failed:
375 DESTROY_IF(pubkey);
376 androidjni_exception_occurred(env);
377 androidjni_detach_thread();
378 return NULL;
379 }
380
381 METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
382 private_charonservice_t *this)
383 {
384 return this->builder;
385 }
386
387 METHOD(charonservice_t, get_network_manager, network_manager_t*,
388 private_charonservice_t *this)
389 {
390 return this->network_manager;
391 }
392
393 /**
394 * Initiate a new connection
395 *
396 * @param settings configuration settings (gets owned)
397 */
398 static void initiate(settings_t *settings)
399 {
400 private_charonservice_t *this = (private_charonservice_t*)charonservice;
401
402 lib->settings->set_str(lib->settings,
403 "charon.plugins.tnc-imc.preferred_language",
404 settings->get_str(settings, "global.language", "en"));
405 /* this is actually the size of the complete IKE/IP packet, so if the MTU
406 * for the TUN devices has to be reduced to pass traffic the IKE packets
407 * will be a bit smaller than necessary as there is no IPsec overhead like
408 * for the tunneled traffic (but compensating that seems like overkill) */
409 lib->settings->set_int(lib->settings,
410 "charon.fragment_size",
411 settings->get_int(settings, "global.mtu",
412 ANDROID_DEFAULT_MTU));
413
414 this->creds->clear(this->creds);
415 DESTROY_IF(this->service);
416 this->service = android_service_create(this->creds, settings);
417 }
418
419 /**
420 * Initialize/deinitialize Android backend
421 */
422 static bool charonservice_register(plugin_t *plugin, plugin_feature_t *feature,
423 bool reg, void *data)
424 {
425 private_charonservice_t *this = (private_charonservice_t*)charonservice;
426 if (reg)
427 {
428 lib->credmgr->add_set(lib->credmgr, &this->creds->set);
429 charon->attributes->add_handler(charon->attributes,
430 &this->attr->handler);
431 }
432 else
433 {
434 lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
435 charon->attributes->remove_handler(charon->attributes,
436 &this->attr->handler);
437 if (this->service)
438 {
439 this->service->destroy(this->service);
440 this->service = NULL;
441 }
442 }
443 return TRUE;
444 }
445
446 /**
447 * Set strongswan.conf options
448 */
449 static void set_options(char *logfile)
450 {
451 lib->settings->set_int(lib->settings,
452 "charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL);
453 /* setup file logger */
454 lib->settings->set_str(lib->settings,
455 "charon.filelog.%s.time_format", "%b %e %T", logfile);
456 lib->settings->set_bool(lib->settings,
457 "charon.filelog.%s.append", FALSE, logfile);
458 lib->settings->set_bool(lib->settings,
459 "charon.filelog.%s.flush_line", TRUE, logfile);
460 lib->settings->set_int(lib->settings,
461 "charon.filelog.%s.default", ANDROID_DEBUG_LEVEL, logfile);
462
463 lib->settings->set_int(lib->settings,
464 "charon.retransmit_tries", ANDROID_RETRASNMIT_TRIES);
465 lib->settings->set_double(lib->settings,
466 "charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT);
467 lib->settings->set_double(lib->settings,
468 "charon.retransmit_base", ANDROID_RETRANSMIT_BASE);
469 lib->settings->set_bool(lib->settings,
470 "charon.initiator_only", TRUE);
471 lib->settings->set_bool(lib->settings,
472 "charon.close_ike_on_child_failure", TRUE);
473 /* setting the source address breaks the VpnService.protect() function which
474 * uses SO_BINDTODEVICE internally. the addresses provided to the kernel as
475 * auxiliary data have precedence over this option causing a routing loop if
476 * the gateway is contained in the VPN routes. alternatively, providing an
477 * explicit device (in addition or instead of the source address) in the
478 * auxiliary data would also work, but we currently don't have that
479 * information */
480 lib->settings->set_bool(lib->settings,
481 "charon.plugins.socket-default.set_source", FALSE);
482 /* the Linux kernel does currently not support UDP encaspulation for IPv6
483 * so lets disable IPv6 for now to avoid issues with dual-stack gateways */
484 lib->settings->set_bool(lib->settings,
485 "charon.plugins.socket-default.use_ipv6", FALSE);
486
487 #ifdef USE_BYOD
488 lib->settings->set_str(lib->settings,
489 "charon.plugins.eap-tnc.protocol", "tnccs-2.0");
490 lib->settings->set_int(lib->settings,
491 "charon.plugins.eap-ttls.max_message_count", 0);
492 lib->settings->set_bool(lib->settings,
493 "android.imc.send_os_info", TRUE);
494 lib->settings->set_str(lib->settings,
495 "libtnccs.tnc_config", "");
496 #endif
497 }
498
499 /**
500 * Initialize the charonservice object
501 */
502 static void charonservice_init(JNIEnv *env, jobject service, jobject builder,
503 jboolean byod)
504 {
505 private_charonservice_t *this;
506 static plugin_feature_t features[] = {
507 PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
508 PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
509 PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create),
510 PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
511 PLUGIN_CALLBACK(charonservice_register, NULL),
512 PLUGIN_PROVIDE(CUSTOM, "android-backend"),
513 PLUGIN_DEPENDS(CUSTOM, "libcharon"),
514 };
515
516 INIT(this,
517 .public = {
518 .update_status = _update_status,
519 .update_imc_state = _update_imc_state,
520 .add_remediation_instr = _add_remediation_instr,
521 .bypass_socket = _bypass_socket,
522 .get_trusted_certificates = _get_trusted_certificates,
523 .get_user_certificate = _get_user_certificate,
524 .get_user_key = _get_user_key,
525 .get_vpnservice_builder = _get_vpnservice_builder,
526 .get_network_manager = _get_network_manager,
527 },
528 .attr = android_attr_create(),
529 .creds = android_creds_create(),
530 .builder = vpnservice_builder_create(builder),
531 .network_manager = network_manager_create(service),
532 .sockets = linked_list_create(),
533 .vpn_service = (*env)->NewGlobalRef(env, service),
534 );
535 charonservice = &this->public;
536
537 lib->plugins->add_static_features(lib->plugins, "androidbridge", features,
538 countof(features), TRUE, NULL, NULL);
539
540 #ifdef USE_BYOD
541 if (byod)
542 {
543 plugin_feature_t byod_features[] = {
544 PLUGIN_CALLBACK(imc_android_register, this->vpn_service),
545 PLUGIN_PROVIDE(CUSTOM, "android-imc"),
546 PLUGIN_DEPENDS(CUSTOM, "android-backend"),
547 PLUGIN_DEPENDS(CUSTOM, "imc-manager"),
548 };
549
550 lib->plugins->add_static_features(lib->plugins, "android-byod",
551 byod_features, countof(byod_features), TRUE, NULL, NULL);
552 }
553 #endif
554 }
555
556 /**
557 * Deinitialize the charonservice object
558 */
559 static void charonservice_deinit(JNIEnv *env)
560 {
561 private_charonservice_t *this = (private_charonservice_t*)charonservice;
562
563 this->network_manager->destroy(this->network_manager);
564 this->sockets->destroy(this->sockets);
565 this->builder->destroy(this->builder);
566 this->creds->destroy(this->creds);
567 this->attr->destroy(this->attr);
568 (*env)->DeleteGlobalRef(env, this->vpn_service);
569 free(this);
570 charonservice = NULL;
571 }
572
573 /**
574 * Handle SIGSEGV/SIGILL signals raised by threads
575 */
576 static void segv_handler(int signal)
577 {
578 dbg_android(DBG_DMN, 1, "thread %u received %d", thread_current_id(),
579 signal);
580 exit(1);
581 }
582
583 /**
584 * Initialize charon and the libraries via JNI
585 */
586 JNI_METHOD(CharonVpnService, initializeCharon, jboolean,
587 jobject builder, jstring jlogfile, jboolean byod)
588 {
589 struct sigaction action;
590 struct utsname utsname;
591 char *logfile, *plugins;
592
593 /* logging for library during initialization, as we have no bus yet */
594 dbg = dbg_android;
595
596 /* initialize library */
597 if (!library_init(NULL, "charon"))
598 {
599 library_deinit();
600 return FALSE;
601 }
602
603 /* set options before initializing other libraries that might read them */
604 logfile = androidjni_convert_jstring(env, jlogfile);
605 set_options(logfile);
606 free(logfile);
607
608 if (!libipsec_init())
609 {
610 libipsec_deinit();
611 library_deinit();
612 return FALSE;
613 }
614
615 if (!libcharon_init())
616 {
617 libcharon_deinit();
618 libipsec_deinit();
619 library_deinit();
620 return FALSE;
621 }
622
623 charon->load_loggers(charon, NULL, FALSE);
624
625 charonservice_init(env, this, builder, byod);
626
627 if (uname(&utsname) != 0)
628 {
629 memset(&utsname, 0, sizeof(utsname));
630 }
631 DBG1(DBG_DMN, "Starting IKE charon daemon (strongSwan "VERSION", %s %s, %s)",
632 utsname.sysname, utsname.release, utsname.machine);
633
634 #ifdef PLUGINS_BYOD
635 if (byod)
636 {
637 plugins = PLUGINS " " PLUGINS_BYOD;
638 }
639 else
640 #endif
641 {
642 plugins = PLUGINS;
643 }
644
645 if (!charon->initialize(charon, plugins))
646 {
647 libcharon_deinit();
648 charonservice_deinit(env);
649 libipsec_deinit();
650 library_deinit();
651 return FALSE;
652 }
653 lib->plugins->status(lib->plugins, LEVEL_CTRL);
654
655 /* add handler for SEGV and ILL etc. */
656 action.sa_handler = segv_handler;
657 action.sa_flags = 0;
658 sigemptyset(&action.sa_mask);
659 sigaction(SIGSEGV, &action, NULL);
660 sigaction(SIGILL, &action, NULL);
661 sigaction(SIGBUS, &action, NULL);
662 action.sa_handler = SIG_IGN;
663 sigaction(SIGPIPE, &action, NULL);
664
665 /* start daemon (i.e. the threads in the thread-pool) */
666 charon->start(charon);
667 return TRUE;
668 }
669
670 /**
671 * Deinitialize charon and all libraries
672 */
673 JNI_METHOD(CharonVpnService, deinitializeCharon, void)
674 {
675 /* deinitialize charon before we destroy our own objects */
676 libcharon_deinit();
677 charonservice_deinit(env);
678 libipsec_deinit();
679 library_deinit();
680 }
681
682 /**
683 * Initiate SA
684 */
685 JNI_METHOD(CharonVpnService, initiate, void,
686 jstring jconfig)
687 {
688 settings_t *settings;
689 char *config;
690
691 config = androidjni_convert_jstring(env, jconfig);
692 settings = settings_create_string(config);
693 initiate(settings);
694 }