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