CharonVpnService provides a function to get trusted certificates via JNI
[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 <android/log.h>
21
22 #include "charonservice.h"
23 #include "android_jni.h"
24 #include "kernel/android_ipsec.h"
25 #include "kernel/android_net.h"
26
27 #include <daemon.h>
28 #include <hydra.h>
29 #include <ipsec.h>
30 #include <library.h>
31 #include <threading/thread.h>
32
33 #define ANDROID_DEBUG_LEVEL 1
34
35 typedef struct private_charonservice_t private_charonservice_t;
36
37 /**
38 * private data of charonservice
39 */
40 struct private_charonservice_t {
41
42 /**
43 * public interface
44 */
45 charonservice_t public;
46
47 /**
48 * CharonVpnService reference
49 */
50 jobject vpn_service;
51 };
52
53 /**
54 * Single instance of charonservice_t.
55 */
56 charonservice_t *charonservice;
57
58 /**
59 * hook in library for debugging messages
60 */
61 extern void (*dbg)(debug_t group, level_t level, char *fmt, ...);
62
63 /**
64 * Logging hook for library logs, using android specific logging
65 */
66 static void dbg_android(debug_t group, level_t level, char *fmt, ...)
67 {
68 va_list args;
69
70 if (level <= ANDROID_DEBUG_LEVEL)
71 {
72 char sgroup[16], buffer[8192];
73 char *current = buffer, *next;
74
75 snprintf(sgroup, sizeof(sgroup), "%N", debug_names, group);
76 va_start(args, fmt);
77 vsnprintf(buffer, sizeof(buffer), fmt, args);
78 va_end(args);
79 while (current)
80 { /* log each line separately */
81 next = strchr(current, '\n');
82 if (next)
83 {
84 *(next++) = '\0';
85 }
86 __android_log_print(ANDROID_LOG_INFO, "charon", "00[%s] %s\n",
87 sgroup, current);
88 current = next;
89 }
90 }
91 }
92
93 METHOD(charonservice_t, update_status, bool,
94 private_charonservice_t *this, android_vpn_state_t code)
95 {
96 JNIEnv *env;
97 jmethodID method_id;
98 bool success = FALSE;
99
100 androidjni_attach_thread(&env);
101
102 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
103 "updateStatus", "(I)V");
104 if (!method_id)
105 {
106 goto failed;
107 }
108 (*env)->CallVoidMethod(env, this->vpn_service, method_id, (jint)code);
109 success = !androidjni_exception_occurred(env);
110
111 failed:
112 androidjni_exception_occurred(env);
113 androidjni_detach_thread();
114 return success;
115 }
116
117 METHOD(charonservice_t, bypass_socket, bool,
118 private_charonservice_t *this, int fd, int family)
119 {
120 JNIEnv *env;
121 jmethodID method_id;
122
123 androidjni_attach_thread(&env);
124
125 method_id = (*env)->GetMethodID(env, android_charonvpnservice_class,
126 "protect", "(I)Z");
127 if (!method_id)
128 {
129 goto failed;
130 }
131 if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
132 {
133 DBG1(DBG_CFG, "VpnService.protect() failed");
134 goto failed;
135 }
136 androidjni_detach_thread();
137 return TRUE;
138
139 failed:
140 androidjni_exception_occurred(env);
141 androidjni_detach_thread();
142 return FALSE;
143 }
144
145 METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
146 private_charonservice_t *this)
147 {
148 JNIEnv *env;
149 jmethodID method_id;
150 jobjectArray jcerts;
151 linked_list_t *list;
152 jsize i;
153
154 androidjni_attach_thread(&env);
155
156 method_id = (*env)->GetMethodID(env,
157 android_charonvpnservice_class,
158 "getTrustedCertificates", "(Ljava/lang/String;)[[B");
159 if (!method_id)
160 {
161 goto failed;
162 }
163 jcerts = (*env)->CallObjectMethod(env, this->vpn_service, method_id, NULL);
164 if (!jcerts)
165 {
166 goto failed;
167 }
168 list = linked_list_create();
169 for (i = 0; i < (*env)->GetArrayLength(env, jcerts); ++i)
170 {
171 chunk_t *ca_cert;
172 jbyteArray jcert;
173
174 ca_cert = malloc_thing(chunk_t);
175 list->insert_last(list, ca_cert);
176
177 jcert = (*env)->GetObjectArrayElement(env, jcerts, i);
178 *ca_cert = chunk_alloc((*env)->GetArrayLength(env, jcert));
179 (*env)->GetByteArrayRegion(env, jcert, 0, ca_cert->len, ca_cert->ptr);
180 (*env)->DeleteLocalRef(env, jcert);
181 }
182 (*env)->DeleteLocalRef(env, jcerts);
183 androidjni_detach_thread();
184 return list;
185
186 failed:
187 androidjni_exception_occurred(env);
188 androidjni_detach_thread();
189 return NULL;
190 }
191
192 /**
193 * Initialize the charonservice object
194 */
195 static void charonservice_init(JNIEnv *env, jobject service)
196 {
197 private_charonservice_t *this;
198 static plugin_feature_t features[] = {
199 PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create),
200 PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
201 PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
202 PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
203 };
204
205 INIT(this,
206 .public = {
207 .update_status = _update_status,
208 .bypass_socket = _bypass_socket,
209 .get_trusted_certificates = _get_trusted_certificates,
210 },
211 .vpn_service = (*env)->NewGlobalRef(env, service),
212 );
213 charonservice = &this->public;
214
215 lib->plugins->add_static_features(lib->plugins, "androidbridge", features,
216 countof(features), TRUE);
217
218 lib->settings->set_int(lib->settings,
219 "charon.plugins.android_log.loglevel", ANDROID_DEBUG_LEVEL);
220 }
221
222 /**
223 * Deinitialize the charonservice object
224 */
225 static void charonservice_deinit(JNIEnv *env)
226 {
227 private_charonservice_t *this = (private_charonservice_t*)charonservice;
228
229 (*env)->DeleteGlobalRef(env, this->vpn_service);
230 free(this);
231 charonservice = NULL;
232 }
233
234 /**
235 * Handle SIGSEGV/SIGILL signals raised by threads
236 */
237 static void segv_handler(int signal)
238 {
239 dbg_android(DBG_DMN, 1, "thread %u received %d", thread_current_id(),
240 signal);
241 exit(1);
242 }
243
244 /**
245 * Initialize charon and the libraries via JNI
246 */
247 JNI_METHOD(CharonVpnService, initializeCharon, void)
248 {
249 struct sigaction action;
250
251 /* logging for library during initialization, as we have no bus yet */
252 dbg = dbg_android;
253
254 /* initialize library */
255 if (!library_init(NULL))
256 {
257 library_deinit();
258 return;
259 }
260
261 if (!libhydra_init("charon"))
262 {
263 libhydra_deinit();
264 library_deinit();
265 return;
266 }
267
268 if (!libipsec_init())
269 {
270 libipsec_deinit();
271 libhydra_deinit();
272 library_deinit();
273 return;
274 }
275
276 charonservice_init(env, this);
277
278 if (!libcharon_init("charon") ||
279 !charon->initialize(charon, PLUGINS))
280 {
281 libcharon_deinit();
282 charonservice_deinit(env);
283 libipsec_deinit();
284 libhydra_deinit();
285 library_deinit();
286 return;
287 }
288
289 /* add handler for SEGV and ILL etc. */
290 action.sa_handler = segv_handler;
291 action.sa_flags = 0;
292 sigemptyset(&action.sa_mask);
293 sigaction(SIGSEGV, &action, NULL);
294 sigaction(SIGILL, &action, NULL);
295 sigaction(SIGBUS, &action, NULL);
296 action.sa_handler = SIG_IGN;
297 sigaction(SIGPIPE, &action, NULL);
298
299 /* start daemon (i.e. the threads in the thread-pool) */
300 charon->start(charon);
301 }
302
303 /**
304 * Deinitialize charon and all libraries
305 */
306 JNI_METHOD(CharonVpnService, deinitializeCharon, void)
307 {
308 libcharon_deinit();
309 charonservice_deinit(env);
310 libipsec_deinit();
311 libhydra_deinit();
312 library_deinit();
313 }
314