f2d8ddce980950b9e12fd8442433dd7b9e4b652f
[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(TRUE, FALSE, "0.0.0.0", FALSE,
268 charon->socket->get_port(charon->socket, FALSE),
269 hostname, FALSE, IKEV2_UDP_PORT);
270 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
271
272 peer_cfg = peer_cfg_create("android", IKEV2, ike_cfg, CERT_SEND_IF_ASKED,
273 UNIQUE_REPLACE, 1, /* keyingtries */
274 36000, 0, /* rekey 10h, reauth none */
275 600, 600, /* jitter, over 10min */
276 TRUE, FALSE, /* mobike, aggressive */
277 0, 0, /* DPD delay, timeout */
278 host_create_from_string("0.0.0.0", 0) /* virt */,
279 NULL, FALSE, NULL, NULL); /* pool, mediation */
280
281 auth = auth_cfg_create();
282 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
283 auth->add(auth, AUTH_RULE_IDENTITY, user);
284 peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
285 auth = auth_cfg_create();
286 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
287 auth->add(auth, AUTH_RULE_IDENTITY, gateway);
288 peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
289
290 child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL,
291 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
292 0, 0, NULL, NULL, 0);
293 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
294 ts = traffic_selector_create_dynamic(0, 0, 65535);
295 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
296 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
297 0, "255.255.255.255", 65535);
298 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
299 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
300
301 /* get us an IKE_SA */
302 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
303 peer_cfg);
304 if (!ike_sa)
305 {
306 peer_cfg->destroy(peer_cfg);
307 send_status(this, VPN_ERROR_CONNECTION_FAILED);
308 return JOB_REQUEUE_NONE;
309 }
310
311 if (!ike_sa->get_peer_cfg(ike_sa))
312 {
313 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
314 }
315 peer_cfg->destroy(peer_cfg);
316
317 /* store the IKE_SA so we can track its progress */
318 this->ike_sa = ike_sa;
319
320 /* confirm that we received the request */
321 send_status(this, i);
322
323 /* get an additional reference because initiate consumes one */
324 child_cfg->get_ref(child_cfg);
325 if (ike_sa->initiate(ike_sa, child_cfg, 0, NULL, NULL) != SUCCESS)
326 {
327 DBG1(DBG_CFG, "failed to initiate tunnel");
328 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
329 ike_sa);
330 send_status(this, VPN_ERROR_CONNECTION_FAILED);
331 return JOB_REQUEUE_NONE;
332 }
333 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
334 return JOB_REQUEUE_NONE;
335 }
336
337 METHOD(android_service_t, destroy, void,
338 private_android_service_t *this)
339 {
340 charon->bus->remove_listener(charon->bus, &this->public.listener);
341 close(this->control);
342 free(this);
343 }
344
345 /**
346 * See header
347 */
348 android_service_t *android_service_create(android_creds_t *creds)
349 {
350 private_android_service_t *this;
351
352 INIT(this,
353 .public = {
354 .listener = {
355 .ike_updown = _ike_updown,
356 .child_state_change = _child_state_change,
357 .child_updown = _child_updown,
358 .ike_rekey = _ike_rekey,
359 },
360 .destroy = _destroy,
361 },
362 .creds = creds,
363 );
364
365 this->control = android_get_control_socket("charon");
366 if (this->control == -1)
367 {
368 DBG1(DBG_CFG, "failed to get Android control socket");
369 free(this);
370 return NULL;
371 }
372
373 if (listen(this->control, 1) < 0)
374 {
375 DBG1(DBG_CFG, "failed to listen on Android control socket: %s",
376 strerror(errno));
377 close(this->control);
378 free(this);
379 return NULL;
380 }
381
382 charon->bus->add_listener(charon->bus, &this->public.listener);
383 lib->processor->queue_job(lib->processor,
384 (job_t*)callback_job_create((callback_job_cb_t)initiate, this,
385 NULL, NULL));
386
387 return &this->public;
388 }
389