Refer to scheduler via hydra and not charon.
[strongswan.git] / src / libcharon / plugins / android / android_service.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <unistd.h>
17 #include <cutils/sockets.h>
18 #include <cutils/properties.h>
19 #include <signal.h>
20
21 #include "android_service.h"
22
23 #include <hydra.h>
24 #include <daemon.h>
25 #include <threading/thread.h>
26 #include <processing/jobs/callback_job.h>
27
28 typedef struct private_android_service_t private_android_service_t;
29
30 /**
31 * private data of Android service
32 */
33 struct private_android_service_t {
34
35 /**
36 * public interface
37 */
38 android_service_t public;
39
40 /**
41 * current IKE_SA
42 */
43 ike_sa_t *ike_sa;
44
45 /**
46 * job that handles requests from the Android control socket
47 */
48 callback_job_t *job;
49
50 /**
51 * android credentials
52 */
53 android_creds_t *creds;
54
55 /**
56 * android control socket
57 */
58 int control;
59
60 };
61
62 /**
63 * Some of the error codes defined in VpnManager.java
64 */
65 typedef enum {
66 /** Error code to indicate an error from authentication. */
67 VPN_ERROR_AUTH = 51,
68 /** Error code to indicate the connection attempt failed. */
69 VPN_ERROR_CONNECTION_FAILED = 101,
70 /** Error code to indicate an error of remote server hanging up. */
71 VPN_ERROR_REMOTE_HUNG_UP = 7,
72 /** Error code to indicate an error of losing connectivity. */
73 VPN_ERROR_CONNECTION_LOST = 103,
74 } android_vpn_errors_t;
75
76 /**
77 * send a status code back to the Android app
78 */
79 static void send_status(private_android_service_t *this, u_char code)
80 {
81 DBG1(DBG_CFG, "status of Android plugin changed: %d", code);
82 send(this->control, &code, 1, 0);
83 }
84
85 METHOD(listener_t, ike_updown, bool,
86 private_android_service_t *this, ike_sa_t *ike_sa, bool up)
87 {
88 /* this callback is only registered during initiation, so if the IKE_SA
89 * goes down we assume an authentication error */
90 if (this->ike_sa == ike_sa && !up)
91 {
92 send_status(this, VPN_ERROR_AUTH);
93 return FALSE;
94 }
95 return TRUE;
96 }
97
98 METHOD(listener_t, child_state_change, bool,
99 private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
100 child_sa_state_t state)
101 {
102 /* this callback is only registered during initiation, so we still have
103 * the control socket open */
104 if (this->ike_sa == ike_sa && state == CHILD_DESTROYING)
105 {
106 send_status(this, VPN_ERROR_CONNECTION_FAILED);
107 return FALSE;
108 }
109 return TRUE;
110 }
111
112 /**
113 * Callback used to shutdown the daemon
114 */
115 static job_requeue_t shutdown_callback(void *data)
116 {
117 kill(0, SIGTERM);
118 return JOB_REQUEUE_NONE;
119 }
120
121 METHOD(listener_t, child_updown, bool,
122 private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
123 bool up)
124 {
125 if (this->ike_sa == ike_sa)
126 {
127 if (up)
128 {
129 /* disable the hooks registered to catch initiation failures */
130 this->public.listener.ike_updown = NULL;
131 this->public.listener.child_state_change = NULL;
132 property_set("vpn.status", "ok");
133 }
134 else
135 {
136 callback_job_t *job;
137 /* the control socket is closed as soon as vpn.status is set to "ok"
138 * and the daemon proxy then only checks for terminated daemons to
139 * detect lost connections, so... */
140 DBG1(DBG_CFG, "connection lost, raising delayed SIGTERM");
141 /* to avoid any conflicts we send the SIGTERM not directly from this
142 * callback, but from a different thread. we also delay it to avoid
143 * a race condition during a regular shutdown */
144 job = callback_job_create(shutdown_callback, NULL, NULL, NULL);
145 hydra->scheduler->schedule_job(hydra->scheduler, (job_t*)job, 1);
146 return FALSE;
147 }
148 }
149 return TRUE;
150 }
151
152 METHOD(listener_t, ike_rekey, bool,
153 private_android_service_t *this, ike_sa_t *old, ike_sa_t *new)
154 {
155 if (this->ike_sa == old)
156 {
157 this->ike_sa = new;
158 }
159 return TRUE;
160 }
161
162 /**
163 * Read a string argument from the Android control socket
164 */
165 static char *read_argument(int fd, u_char length)
166 {
167 int offset = 0;
168 char *data = malloc(length + 1);
169 while (offset < length)
170 {
171 int n = recv(fd, &data[offset], length - offset, 0);
172 if (n < 0)
173 {
174 DBG1(DBG_CFG, "failed to read argument from Android"
175 " control socket: %s", strerror(errno));
176 free(data);
177 return NULL;
178 }
179 offset += n;
180 }
181 data[length] = '\0';
182 DBG3(DBG_CFG, "received argument from Android control socket: %s", data);
183 return data;
184 }
185
186 /**
187 * handle the request received from the Android control socket
188 */
189 static job_requeue_t initiate(private_android_service_t *this)
190 {
191 bool oldstate;
192 int fd, i = 0;
193 char *hostname = NULL, *cacert = NULL, *username = NULL, *password = NULL;
194 identification_t *gateway = NULL, *user = NULL;
195 ike_cfg_t *ike_cfg;
196 peer_cfg_t *peer_cfg;
197 child_cfg_t *child_cfg;
198 traffic_selector_t *ts;
199 ike_sa_t *ike_sa;
200 auth_cfg_t *auth;
201 lifetime_cfg_t lifetime = {
202 .time = {
203 .life = 10800, /* 3h */
204 .rekey = 10200, /* 2h50min */
205 .jitter = 300 /* 5min */
206 }
207 };
208
209 fd = accept(this->control, NULL, 0);
210 if (fd < 0)
211 {
212 DBG1(DBG_CFG, "accept on Android control socket failed: %s",
213 strerror(errno));
214 return JOB_REQUEUE_NONE;
215 }
216 /* the original control socket is not used anymore */
217 close(this->control);
218 this->control = fd;
219
220 while (TRUE)
221 {
222 u_char length;
223 if (recv(fd, &length, 1, 0) != 1)
224 {
225 DBG1(DBG_CFG, "failed to read from Android control socket: %s",
226 strerror(errno));
227 return JOB_REQUEUE_NONE;
228 }
229
230 if (length == 0xFF)
231 { /* last argument */
232 break;
233 }
234 else
235 {
236 switch (i++)
237 {
238 case 0: /* gateway */
239 hostname = read_argument(fd, length);
240 break;
241 case 1: /* CA certificate name */
242 cacert = read_argument(fd, length);
243 break;
244 case 2: /* username */
245 username = read_argument(fd, length);
246 break;
247 case 3: /* password */
248 password = read_argument(fd, length);
249 break;
250 }
251 }
252 }
253
254 if (cacert)
255 {
256 if (!this->creds->add_certificate(this->creds, cacert))
257 {
258 DBG1(DBG_CFG, "failed to load CA certificate");
259 }
260 /* if this is a server cert we could use the cert subject as id
261 * but we have to test first if that possible to configure */
262 }
263
264 gateway = identification_create_from_string(hostname);
265 DBG1(DBG_CFG, "using CA certificate, gateway identitiy '%Y'", gateway);
266
267 if (username)
268 {
269 user = identification_create_from_string(username);
270 this->creds->set_username_password(this->creds, user, password);
271 }
272
273 ike_cfg = ike_cfg_create(TRUE, FALSE, "0.0.0.0", IKEV2_UDP_PORT,
274 hostname, IKEV2_UDP_PORT);
275 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
276
277 peer_cfg = peer_cfg_create("android", 2, ike_cfg, CERT_SEND_IF_ASKED,
278 UNIQUE_REPLACE, 1, /* keyingtries */
279 36000, 0, /* rekey 10h, reauth none */
280 600, 600, /* jitter, over 10min */
281 TRUE, 0, /* mobike, DPD */
282 host_create_from_string("0.0.0.0", 0) /* virt */,
283 NULL, FALSE, NULL, NULL); /* pool, mediation */
284
285 auth = auth_cfg_create();
286 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
287 auth->add(auth, AUTH_RULE_IDENTITY, user);
288 peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
289 auth = auth_cfg_create();
290 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
291 auth->add(auth, AUTH_RULE_IDENTITY, gateway);
292 peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
293
294 child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL,
295 ACTION_NONE, ACTION_NONE, FALSE, 0, 0,
296 NULL, NULL);
297 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
298 ts = traffic_selector_create_dynamic(0, 0, 65535);
299 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
300 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
301 0, "255.255.255.255", 65535);
302 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
303 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
304 /* get an additional reference because initiate consumes one */
305 child_cfg->get_ref(child_cfg);
306
307 /* get us an IKE_SA */
308 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
309 peer_cfg);
310 if (!ike_sa->get_peer_cfg(ike_sa))
311 {
312 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
313 }
314 peer_cfg->destroy(peer_cfg);
315
316 /* store the IKE_SA so we can track its progress */
317 this->ike_sa = ike_sa;
318
319 /* confirm that we received the request */
320 send_status(this, i);
321
322 if (ike_sa->initiate(ike_sa, child_cfg, 0, NULL, NULL) != SUCCESS)
323 {
324 DBG1(DBG_CFG, "failed to initiate tunnel");
325 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
326 ike_sa);
327 send_status(this, VPN_ERROR_CONNECTION_FAILED);
328 return JOB_REQUEUE_NONE;
329 }
330 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
331 return JOB_REQUEUE_NONE;
332 }
333
334 METHOD(android_service_t, destroy, void,
335 private_android_service_t *this)
336 {
337 charon->bus->remove_listener(charon->bus, &this->public.listener);
338 close(this->control);
339 free(this);
340 }
341
342 /**
343 * See header
344 */
345 android_service_t *android_service_create(android_creds_t *creds)
346 {
347 private_android_service_t *this;
348
349 INIT(this,
350 .public = {
351 .listener = {
352 .ike_updown = _ike_updown,
353 .child_state_change = _child_state_change,
354 .child_updown = _child_updown,
355 .ike_rekey = _ike_rekey,
356 },
357 .destroy = _destroy,
358 },
359 .creds = creds,
360 );
361
362 this->control = android_get_control_socket("charon");
363 if (this->control == -1)
364 {
365 DBG1(DBG_CFG, "failed to get Android control socket");
366 free(this);
367 return NULL;
368 }
369
370 if (listen(this->control, 1) < 0)
371 {
372 DBG1(DBG_CFG, "failed to listen on Android control socket: %s",
373 strerror(errno));
374 close(this->control);
375 free(this);
376 return NULL;
377 }
378
379 charon->bus->add_listener(charon->bus, &this->public.listener);
380 this->job = callback_job_create((callback_job_cb_t)initiate, this,
381 NULL, NULL);
382 hydra->processor->queue_job(hydra->processor, (job_t*)this->job);
383
384 return &this->public;
385 }
386