android: Add a simple HTTP(S) fetcher for CRLs
[strongswan.git] / src / frontends / android / app / src / main / jni / libandroidbridge / charonservice.c
1 /*
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
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_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"
33
34 #ifdef USE_BYOD
35 #include "byod/imc_android.h"
36 #endif
37
38 #include <daemon.h>
39 #include <ipsec.h>
40 #include <library.h>
41 #include <threading/thread.h>
42
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
48
49 typedef struct private_charonservice_t private_charonservice_t;
50
51 /**
52 * private data of charonservice
53 */
54 struct private_charonservice_t {
55
56 /**
57 * public interface
58 */
59 charonservice_t public;
60
61 /**
62 * android_attr instance
63 */
64 android_attr_t *attr;
65
66 /**
67 * android_creds instance
68 */
69 android_creds_t *creds;
70
71 /**
72 * android_service instance
73 */
74 android_service_t *service;
75
76 /**
77 * VpnService builder (accessed via JNI)
78 */
79 vpnservice_builder_t *builder;
80
81 /**
82 * NetworkManager instance (accessed via JNI)
83 */
84 network_manager_t *network_manager;
85
86 /**
87 * CharonVpnService reference
88 */
89 jobject vpn_service;
90
91 /**
92 * Sockets that were bypassed and we keep track for
93 */
94 linked_list_t *sockets;
95 };
96
97 /**
98 * Single instance of charonservice_t.
99 */
100 charonservice_t *charonservice;
101
102 /**
103 * hook in library for debugging messages
104 */
105 extern void (*dbg)(debug_t group, level_t level, char *fmt, ...);
106
107 /**
108 * Logging hook for library logs, using android specific logging
109 */
110 static void dbg_android(debug_t group, level_t level, char *fmt, ...)
111 {
112 va_list args;
113
114 if (level <= ANDROID_DEBUG_LEVEL)
115 {
116 char sgroup[16], buffer[8192];
117 char *current = buffer, *next;
118
119 snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group);
120 va_start(args, fmt);
121 vsnprintf(buffer, sizeof(buffer), fmt, args);
122 va_end(args);
123 while (current)
124 { /* log each line separately */
125 next = strchr(current, '\n');
126 if (next)
127 {
128 *(next++) = '\0';
129 }
130 __android_log_print(ANDROID_LOG_INFO, "charon", "00[%s] %s\n",
131 sgroup, current);
132 current = next;
133 }
134 }
135 }
136
137 METHOD(charonservice_t, update_status, bool,
138 private_charonservice_t *this, android_vpn_state_t code)
139 {
140 JNIEnv *env;
141 jmethodID method_id;
142 bool success = FALSE;
143
144 androidjni_attach_thread(&env);
145
146 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
147 "updateStatus", "(I)V");
148 if (!method_id)
149 {
150 goto failed;
151 }
152 (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)code);
153 success = !androidjni_exception_occurred(env);
154
155 failed:
156 androidjni_exception_occurred(env);
157 androidjni_detach_thread();
158 return success;
159 }
160
161 METHOD(charonservice_t, update_imc_state, bool,
162 private_charonservice_t *this, android_imc_state_t state)
163 {
164 JNIEnv *env;
165 jmethodID method_id;
166 bool success = FALSE;
167
168 androidjni_attach_thread(&env);
169
170 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
171 "updateImcState", "(I)V");
172 if (!method_id)
173 {
174 goto failed;
175 }
176 (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)state);
177 success = !androidjni_exception_occurred(env);
178
179 failed:
180 androidjni_exception_occurred(env);
181 androidjni_detach_thread();
182 return success;
183 }
184
185 METHOD(charonservice_t, add_remediation_instr, bool,
186 private_charonservice_t *this, char *instr)
187 {
188 JNIEnv *env;
189 jmethodID method_id;
190 jstring jinstr;
191 bool success = FALSE;
192
193 androidjni_attach_thread(&env);
194
195 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
196 "addRemediationInstruction",
197 "(Ljava/lang/String;)V");
198 if (!method_id)
199 {
200 goto failed;
201 }
202 jinstr = (*env)->NewStringUTF(env, instr);
203 if (!jinstr)
204 {
205 goto failed;
206 }
207 (*env)->CallVoidMethod(env, this->vpn_service, method_id, jinstr);
208 success = !androidjni_exception_occurred(env);
209
210 failed:
211 androidjni_exception_occurred(env);
212 androidjni_detach_thread();
213 return success;
214 }
215
216 /**
217 * Bypass a single socket
218 */
219 static bool bypass_single_socket(private_charonservice_t *this, int fd)
220 {
221 JNIEnv *env;
222 jmethodID method_id;
223
224 androidjni_attach_thread(&env);
225
226 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
227 "protect", "(I)Z");
228 if (!method_id)
229 {
230 goto failed;
231 }
232 if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
233 {
234 DBG2(DBG_KNL, "VpnService.protect() failed");
235 goto failed;
236 }
237 androidjni_detach_thread();
238 return TRUE;
239
240 failed:
241 androidjni_exception_occurred(env);
242 androidjni_detach_thread();
243 return FALSE;
244 }
245
246 CALLBACK(bypass_single_socket_cb, void,
247 intptr_t fd, va_list args)
248 {
249 private_charonservice_t *this;
250
251 VA_ARGS_VGET(args, this);
252 bypass_single_socket(this, fd);
253 }
254
255 METHOD(charonservice_t, bypass_socket, bool,
256 private_charonservice_t *this, int fd, int family)
257 {
258 if (fd >= 0)
259 {
260 this->sockets->insert_last(this->sockets, (void*)(intptr_t)fd);
261 return bypass_single_socket(this, fd);
262 }
263 this->sockets->invoke_function(this->sockets, bypass_single_socket_cb, this);
264 return TRUE;
265 }
266
267 /**
268 * Converts the given Java array of byte arrays (byte[][]) to a linked list
269 * of chunk_t objects.
270 */
271 static linked_list_t *convert_array_of_byte_arrays(JNIEnv *env,
272 jobjectArray jarray)
273 {
274 linked_list_t *list;
275 jsize i;
276
277 list = linked_list_create();
278 for (i = 0; i < (*env)->GetArrayLength(env, jarray); ++i)
279 {
280 chunk_t *chunk;
281 jbyteArray jbytearray;
282
283 chunk = malloc_thing(chunk_t);
284 list->insert_last(list, chunk);
285
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);
290 }
291 return list;
292 }
293
294 METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
295 private_charonservice_t *this)
296 {
297 JNIEnv *env;
298 jmethodID method_id;
299 jobjectArray jcerts;
300 linked_list_t *list;
301
302 androidjni_attach_thread(&env);
303
304 method_id = (*env)->GetMethodID(env,
305 android_charonvpnservice_class,
306 "getTrustedCertificates", "()[[B");
307 if (!method_id)
308 {
309 goto failed;
310 }
311 jcerts = (*env)->CallObjectMethod(env, this->vpn_service, method_id);
312 if (!jcerts || androidjni_exception_occurred(env))
313 {
314 goto failed;
315 }
316 list = convert_array_of_byte_arrays(env, jcerts);
317 androidjni_detach_thread();
318 return list;
319
320 failed:
321 androidjni_exception_occurred(env);
322 androidjni_detach_thread();
323 return NULL;
324 }
325
326 METHOD(charonservice_t, get_user_certificate, linked_list_t*,
327 private_charonservice_t *this)
328 {
329 JNIEnv *env;
330 jmethodID method_id;
331 jobjectArray jencodings;
332 linked_list_t *list;
333
334 androidjni_attach_thread(&env);
335
336 method_id = (*env)->GetMethodID(env,
337 android_charonvpnservice_class,
338 "getUserCertificate", "()[[B");
339 if (!method_id)
340 {
341 goto failed;
342 }
343 jencodings = (*env)->CallObjectMethod(env, this->vpn_service, method_id);
344 if (!jencodings || androidjni_exception_occurred(env))
345 {
346 goto failed;
347 }
348 list = convert_array_of_byte_arrays(env, jencodings);
349 androidjni_detach_thread();
350 return list;
351
352 failed:
353 androidjni_exception_occurred(env);
354 androidjni_detach_thread();
355 return NULL;
356 }
357
358 METHOD(charonservice_t, get_user_key, private_key_t*,
359 private_charonservice_t *this, public_key_t *pubkey)
360 {
361 JNIEnv *env;
362 jmethodID method_id;
363 private_key_t *key;
364 jobject jkey;
365
366 androidjni_attach_thread(&env);
367
368 method_id = (*env)->GetMethodID(env,
369 android_charonvpnservice_class,
370 "getUserKey", "()Ljava/security/PrivateKey;");
371 if (!method_id)
372 {
373 goto failed;
374 }
375 jkey = (*env)->CallObjectMethod(env, this->vpn_service, method_id);
376 if (!jkey || androidjni_exception_occurred(env))
377 {
378 goto failed;
379 }
380 key = android_private_key_create(jkey, pubkey);
381 androidjni_detach_thread();
382 return key;
383
384 failed:
385 DESTROY_IF(pubkey);
386 androidjni_exception_occurred(env);
387 androidjni_detach_thread();
388 return NULL;
389 }
390
391 METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
392 private_charonservice_t *this)
393 {
394 return this->builder;
395 }
396
397 METHOD(charonservice_t, get_network_manager, network_manager_t*,
398 private_charonservice_t *this)
399 {
400 return this->network_manager;
401 }
402
403 /**
404 * Initiate a new connection
405 *
406 * @param settings configuration settings (gets owned)
407 */
408 static void initiate(settings_t *settings)
409 {
410 private_charonservice_t *this = (private_charonservice_t*)charonservice;
411
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));
423
424 this->creds->clear(this->creds);
425 DESTROY_IF(this->service);
426 this->service = android_service_create(this->creds, settings);
427 }
428
429 /**
430 * Initialize/deinitialize Android backend
431 */
432 static bool charonservice_register(plugin_t *plugin, plugin_feature_t *feature,
433 bool reg, void *data)
434 {
435 private_charonservice_t *this = (private_charonservice_t*)charonservice;
436 if (reg)
437 {
438 lib->credmgr->add_set(lib->credmgr, &this->creds->set);
439 charon->attributes->add_handler(charon->attributes,
440 &this->attr->handler);
441 }
442 else
443 {
444 lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
445 charon->attributes->remove_handler(charon->attributes,
446 &this->attr->handler);
447 if (this->service)
448 {
449 this->service->destroy(this->service);
450 this->service = NULL;
451 }
452 }
453 return TRUE;
454 }
455
456 /**
457 * Set strongswan.conf options
458 */
459 static void set_options(char *logfile)
460 {
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);
472
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
492 * information */
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);
499
500 #ifdef USE_BYOD
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", "");
509 #endif
510 }
511
512 /**
513 * Initialize the charonservice object
514 */
515 static void charonservice_init(JNIEnv *env, jobject service, jobject builder,
516 jboolean byod)
517 {
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://"),
530 };
531
532 INIT(this,
533 .public = {
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,
543 },
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),
550 );
551 charonservice = &this->public;
552
553 lib->plugins->add_static_features(lib->plugins, "androidbridge", features,
554 countof(features), TRUE, NULL, NULL);
555
556 #ifdef USE_BYOD
557 if (byod)
558 {
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"),
564 };
565
566 lib->plugins->add_static_features(lib->plugins, "android-byod",
567 byod_features, countof(byod_features), TRUE, NULL, NULL);
568 }
569 #endif
570 }
571
572 /**
573 * Deinitialize the charonservice object
574 */
575 static void charonservice_deinit(JNIEnv *env)
576 {
577 private_charonservice_t *this = (private_charonservice_t*)charonservice;
578
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);
585 free(this);
586 charonservice = NULL;
587 }
588
589 /**
590 * Handle SIGSEGV/SIGILL signals raised by threads
591 */
592 static void segv_handler(int signal)
593 {
594 dbg_android(DBG_DMN, 1, "thread %u received %d", thread_current_id(),
595 signal);
596 exit(1);
597 }
598
599 /**
600 * Initialize charon and the libraries via JNI
601 */
602 JNI_METHOD(CharonVpnService, initializeCharon, jboolean,
603 jobject builder, jstring jlogfile, jboolean byod)
604 {
605 struct sigaction action;
606 struct utsname utsname;
607 char *logfile, *plugins;
608
609 /* logging for library during initialization, as we have no bus yet */
610 dbg = dbg_android;
611
612 /* initialize library */
613 if (!library_init(NULL, "charon"))
614 {
615 library_deinit();
616 return FALSE;
617 }
618
619 /* set options before initializing other libraries that might read them */
620 logfile = androidjni_convert_jstring(env, jlogfile);
621 set_options(logfile);
622 free(logfile);
623
624 if (!libipsec_init())
625 {
626 libipsec_deinit();
627 library_deinit();
628 return FALSE;
629 }
630
631 if (!libcharon_init())
632 {
633 libcharon_deinit();
634 libipsec_deinit();
635 library_deinit();
636 return FALSE;
637 }
638
639 charon->load_loggers(charon);
640
641 charonservice_init(env, this, builder, byod);
642
643 if (uname(&utsname) != 0)
644 {
645 memset(&utsname, 0, sizeof(utsname));
646 }
647 DBG1(DBG_DMN, "Starting IKE charon daemon (strongSwan "VERSION", %s %s, %s)",
648 utsname.sysname, utsname.release, utsname.machine);
649
650 #ifdef PLUGINS_BYOD
651 if (byod)
652 {
653 plugins = PLUGINS " " PLUGINS_BYOD;
654 }
655 else
656 #endif
657 {
658 plugins = PLUGINS;
659 }
660
661 if (!charon->initialize(charon, plugins))
662 {
663 libcharon_deinit();
664 charonservice_deinit(env);
665 libipsec_deinit();
666 library_deinit();
667 return FALSE;
668 }
669 lib->plugins->status(lib->plugins, LEVEL_CTRL);
670
671 /* add handler for SEGV and ILL etc. */
672 action.sa_handler = segv_handler;
673 action.sa_flags = 0;
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);
680
681 /* start daemon (i.e. the threads in the thread-pool) */
682 charon->start(charon);
683 return TRUE;
684 }
685
686 /**
687 * Deinitialize charon and all libraries
688 */
689 JNI_METHOD(CharonVpnService, deinitializeCharon, void)
690 {
691 /* deinitialize charon before we destroy our own objects */
692 libcharon_deinit();
693 charonservice_deinit(env);
694 libipsec_deinit();
695 library_deinit();
696 }
697
698 /**
699 * Initiate SA
700 */
701 JNI_METHOD(CharonVpnService, initiate, void,
702 jstring jconfig)
703 {
704 settings_t *settings;
705 char *config;
706
707 config = androidjni_convert_jstring(env, jconfig);
708 settings = settings_create_string(config);
709 initiate(settings);
710 }