802e087ec624ba27d5c2a853d1b942106ba63fbe
[strongswan.git] / src / frontends / android / jni / libandroidbridge / charonservice.c
1 /*
2 * Copyright (C) 2012 Giuliano Grassi
3 * Copyright (C) 2012 Ralf Sager
4 * Copyright (C) 2012 Tobias Brunner
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
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"
30
31 #include <daemon.h>
32 #include <hydra.h>
33 #include <ipsec.h>
34 #include <library.h>
35 #include <threading/thread.h>
36
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
41
42 typedef struct private_charonservice_t private_charonservice_t;
43
44 /**
45 * private data of charonservice
46 */
47 struct private_charonservice_t {
48
49 /**
50 * public interface
51 */
52 charonservice_t public;
53
54 /**
55 * android_attr instance
56 */
57 android_attr_t *attr;
58
59 /**
60 * android_creds instance
61 */
62 android_creds_t *creds;
63
64 /**
65 * android_service instance
66 */
67 android_service_t *service;
68
69 /**
70 * VpnService builder (accessed via JNI)
71 */
72 vpnservice_builder_t *builder;
73
74 /**
75 * CharonVpnService reference
76 */
77 jobject vpn_service;
78 };
79
80 /**
81 * Single instance of charonservice_t.
82 */
83 charonservice_t *charonservice;
84
85 /**
86 * hook in library for debugging messages
87 */
88 extern void (*dbg)(debug_t group, level_t level, char *fmt, ...);
89
90 /**
91 * Logging hook for library logs, using android specific logging
92 */
93 static void dbg_android(debug_t group, level_t level, char *fmt, ...)
94 {
95 va_list args;
96
97 if (level <= ANDROID_DEBUG_LEVEL)
98 {
99 char sgroup[16], buffer[8192];
100 char *current = buffer, *next;
101
102 snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group);
103 va_start(args, fmt);
104 vsnprintf(buffer, sizeof(buffer), fmt, args);
105 va_end(args);
106 while (current)
107 { /* log each line separately */
108 next = strchr(current, '\n');
109 if (next)
110 {
111 *(next++) = '\0';
112 }
113 __android_log_print(ANDROID_LOG_INFO, "charon", "00[%s] %s\n",
114 sgroup, current);
115 current = next;
116 }
117 }
118 }
119
120 METHOD(charonservice_t, update_status, bool,
121 private_charonservice_t *this, android_vpn_state_t code)
122 {
123 JNIEnv *env;
124 jmethodID method_id;
125 bool success = FALSE;
126
127 androidjni_attach_thread(&env);
128
129 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
130 "updateStatus", "(I)V");
131 if (!method_id)
132 {
133 goto failed;
134 }
135 (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)code);
136 success = !androidjni_exception_occurred(env);
137
138 failed:
139 androidjni_exception_occurred(env);
140 androidjni_detach_thread();
141 return success;
142 }
143
144 METHOD(charonservice_t, bypass_socket, bool,
145 private_charonservice_t *this, int fd, int family)
146 {
147 JNIEnv *env;
148 jmethodID method_id;
149
150 androidjni_attach_thread(&env);
151
152 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
153 "protect", "(I)Z");
154 if (!method_id)
155 {
156 goto failed;
157 }
158 if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
159 {
160 DBG1(DBG_CFG, "VpnService.protect() failed");
161 goto failed;
162 }
163 androidjni_detach_thread();
164 return TRUE;
165
166 failed:
167 androidjni_exception_occurred(env);
168 androidjni_detach_thread();
169 return FALSE;
170 }
171
172 METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
173 private_charonservice_t *this)
174 {
175 JNIEnv *env;
176 jmethodID method_id;
177 jobjectArray jcerts;
178 linked_list_t *list;
179 jsize i;
180
181 androidjni_attach_thread(&env);
182
183 method_id = (*env)->GetMethodID(env,
184 android_charonvpnservice_class,
185 "getTrustedCertificates", "(Ljava/lang/String;)[[B");
186 if (!method_id)
187 {
188 goto failed;
189 }
190 jcerts = (*env)->CallObjectMethod(env, this->vpn_service, method_id, NULL);
191 if (!jcerts)
192 {
193 goto failed;
194 }
195 list = linked_list_create();
196 for (i = 0; i < (*env)->GetArrayLength(env, jcerts); ++i)
197 {
198 chunk_t *ca_cert;
199 jbyteArray jcert;
200
201 ca_cert = malloc_thing(chunk_t);
202 list->insert_last(list, ca_cert);
203
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);
208 }
209 (*env)->DeleteLocalRef(env, jcerts);
210 androidjni_detach_thread();
211 return list;
212
213 failed:
214 androidjni_exception_occurred(env);
215 androidjni_detach_thread();
216 return NULL;
217 }
218
219 METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
220 private_charonservice_t *this)
221 {
222 return this->builder;
223 }
224
225 /**
226 * Initiate a new connection
227 *
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)
232 */
233 static void initiate(char *local, char *gateway, char *username, char *password)
234 {
235 private_charonservice_t *this = (private_charonservice_t*)charonservice;
236
237 this->creds->clear(this->creds);
238 this->creds->add_username_password(this->creds, username, password);
239 memwipe(password, strlen(password));
240 free(password);
241
242 DESTROY_IF(this->service);
243 this->service = android_service_create(local, gateway, username);
244 }
245
246 /**
247 * Initialize/deinitialize Android backend
248 */
249 static bool charonservice_register(void *plugin, plugin_feature_t *feature,
250 bool reg, void *data)
251 {
252 private_charonservice_t *this = (private_charonservice_t*)charonservice;
253 if (reg)
254 {
255 lib->credmgr->add_set(lib->credmgr, &this->creds->set);
256 hydra->attributes->add_handler(hydra->attributes,
257 &this->attr->handler);
258 }
259 else
260 {
261 lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
262 hydra->attributes->remove_handler(hydra->attributes,
263 &this->attr->handler);
264 if (this->service)
265 {
266 this->service->destroy(this->service);
267 this->service = NULL;
268 }
269 }
270 return TRUE;
271 }
272
273 /**
274 * Initialize the charonservice object
275 */
276 static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
277 {
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"),
287 };
288
289 INIT(this,
290 .public = {
291 .update_status = _update_status,
292 .bypass_socket = _bypass_socket,
293 .get_trusted_certificates = _get_trusted_certificates,
294 .get_vpnservice_builder = _get_vpnservice_builder,
295 },
296 .attr = android_attr_create(),
297 .creds = android_creds_create(),
298 .builder = vpnservice_builder_create(builder),
299 .vpn_service = (*env)->NewGlobalRef(env, service),
300 );
301 charonservice = &this->public;
302
303 lib->plugins->add_static_features(lib->plugins, "androidbridge", features,
304 countof(features), TRUE);
305
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
322 * information */
323 lib->settings->set_bool(lib->settings,
324 "charon.plugins.socket-default.set_source", FALSE);
325 }
326
327 /**
328 * Deinitialize the charonservice object
329 */
330 static void charonservice_deinit(JNIEnv *env)
331 {
332 private_charonservice_t *this = (private_charonservice_t*)charonservice;
333
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);
338 free(this);
339 charonservice = NULL;
340 }
341
342 /**
343 * Handle SIGSEGV/SIGILL signals raised by threads
344 */
345 static void segv_handler(int signal)
346 {
347 dbg_android(DBG_DMN, 1, "thread %u received %d", thread_current_id(),
348 signal);
349 exit(1);
350 }
351
352 /**
353 * Initialize charon and the libraries via JNI
354 */
355 JNI_METHOD(CharonVpnService, initializeCharon, void,
356 jobject builder)
357 {
358 struct sigaction action;
359 struct utsname utsname;
360
361 /* logging for library during initialization, as we have no bus yet */
362 dbg = dbg_android;
363
364 /* initialize library */
365 if (!library_init(NULL))
366 {
367 library_deinit();
368 return;
369 }
370
371 if (!libhydra_init("charon"))
372 {
373 libhydra_deinit();
374 library_deinit();
375 return;
376 }
377
378 if (!libipsec_init())
379 {
380 libipsec_deinit();
381 libhydra_deinit();
382 library_deinit();
383 return;
384 }
385
386 if (!libcharon_init("charon"))
387 {
388 libcharon_deinit();
389 libipsec_deinit();
390 libhydra_deinit();
391 library_deinit();
392 return;
393 }
394
395 charonservice_init(env, this, builder);
396
397 if (uname(&utsname) != 0)
398 {
399 memset(&utsname, 0, sizeof(utsname));
400 }
401 DBG1(DBG_DMN, "Starting IKE charon daemon (strongSwan "VERSION", %s %s, %s)",
402 utsname.sysname, utsname.release, utsname.machine);
403
404 if (!charon->initialize(charon, PLUGINS))
405 {
406 libcharon_deinit();
407 charonservice_deinit(env);
408 libipsec_deinit();
409 libhydra_deinit();
410 library_deinit();
411 return;
412 }
413
414 /* add handler for SEGV and ILL etc. */
415 action.sa_handler = segv_handler;
416 action.sa_flags = 0;
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);
423
424 /* start daemon (i.e. the threads in the thread-pool) */
425 charon->start(charon);
426 }
427
428 /**
429 * Deinitialize charon and all libraries
430 */
431 JNI_METHOD(CharonVpnService, deinitializeCharon, void)
432 {
433 /* deinitialize charon before we destroy our own objects */
434 libcharon_deinit();
435 charonservice_deinit(env);
436 libipsec_deinit();
437 libhydra_deinit();
438 library_deinit();
439 }
440
441 /**
442 * Initiate SA
443 */
444 JNI_METHOD(CharonVpnService, initiate, void,
445 jstring jlocal_address, jstring jgateway, jstring jusername,
446 jstring jpassword)
447 {
448 char *local_address, *gateway, *username, *password;
449
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);
454
455 initiate(local_address, gateway, username, password);
456 }