2 * Copyright (C) 2010 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
17 #include <cutils/sockets.h>
18 #include <cutils/properties.h>
21 #include "android_service.h"
24 #include <threading/thread.h>
25 #include <processing/jobs/callback_job.h>
27 typedef struct private_android_service_t private_android_service_t
;
30 * private data of Android service
32 struct private_android_service_t
{
37 android_service_t
public;
45 * job that handles requests from the Android control socket
52 android_creds_t
*creds
;
55 * android control socket
62 * Some of the error codes defined in VpnManager.java
65 /** Error code to indicate an error from authentication. */
67 /** Error code to indicate the connection attempt failed. */
68 VPN_ERROR_CONNECTION_FAILED
= 101,
69 /** Error code to indicate an error of remote server hanging up. */
70 VPN_ERROR_REMOTE_HUNG_UP
= 7,
71 /** Error code to indicate an error of losing connectivity. */
72 VPN_ERROR_CONNECTION_LOST
= 103,
73 } android_vpn_errors_t
;
76 * send a status code back to the Android app
78 static void send_status(private_android_service_t
*this, u_char code
)
80 DBG1(DBG_CFG
, "status of Android plugin changed: %d", code
);
81 send(this->control
, &code
, 1, 0);
84 METHOD(listener_t
, ike_updown
, bool,
85 private_android_service_t
*this, ike_sa_t
*ike_sa
, bool up
)
87 /* this callback is only registered during initiation, so if the IKE_SA
88 * goes down we assume an authentication error */
89 if (this->ike_sa
== ike_sa
&& !up
)
91 send_status(this, VPN_ERROR_AUTH
);
97 METHOD(listener_t
, child_state_change
, bool,
98 private_android_service_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
99 child_sa_state_t state
)
101 /* this callback is only registered during initiation, so we still have
102 * the control socket open */
103 if (this->ike_sa
== ike_sa
&& state
== CHILD_DESTROYING
)
105 send_status(this, VPN_ERROR_CONNECTION_FAILED
);
112 * Callback used to shutdown the daemon
114 static job_requeue_t
shutdown_callback(void *data
)
117 return JOB_REQUEUE_NONE
;
120 METHOD(listener_t
, child_updown
, bool,
121 private_android_service_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
124 if (this->ike_sa
== ike_sa
)
128 /* disable the hooks registered to catch initiation failures */
129 this->public.listener
.ike_updown
= NULL
;
130 this->public.listener
.child_state_change
= NULL
;
131 property_set("vpn.status", "ok");
136 /* the control socket is closed as soon as vpn.status is set to "ok"
137 * and the daemon proxy then only checks for terminated daemons to
138 * detect lost connections, so... */
139 DBG1(DBG_CFG
, "connection lost, raising delayed SIGTERM");
140 /* to avoid any conflicts we send the SIGTERM not directly from this
141 * callback, but from a different thread. we also delay it to avoid
142 * a race condition during a regular shutdown */
143 job
= callback_job_create(shutdown_callback
, NULL
, NULL
, NULL
);
144 lib
->scheduler
->schedule_job(lib
->scheduler
, (job_t
*)job
, 1);
151 METHOD(listener_t
, ike_rekey
, bool,
152 private_android_service_t
*this, ike_sa_t
*old
, ike_sa_t
*new)
154 if (this->ike_sa
== old
)
162 * Read a string argument from the Android control socket
164 static char *read_argument(int fd
, u_char length
)
167 char *data
= malloc(length
+ 1);
168 while (offset
< length
)
170 int n
= recv(fd
, &data
[offset
], length
- offset
, 0);
173 DBG1(DBG_CFG
, "failed to read argument from Android"
174 " control socket: %s", strerror(errno
));
181 DBG3(DBG_CFG
, "received argument from Android control socket: %s", data
);
186 * handle the request received from the Android control socket
188 static job_requeue_t
initiate(private_android_service_t
*this)
192 char *hostname
= NULL
, *cacert
= NULL
, *username
= NULL
, *password
= NULL
;
193 identification_t
*gateway
= NULL
, *user
= NULL
;
195 peer_cfg_t
*peer_cfg
;
196 child_cfg_t
*child_cfg
;
197 traffic_selector_t
*ts
;
200 lifetime_cfg_t lifetime
= {
202 .life
= 10800, /* 3h */
203 .rekey
= 10200, /* 2h50min */
204 .jitter
= 300 /* 5min */
208 fd
= accept(this->control
, NULL
, 0);
211 DBG1(DBG_CFG
, "accept on Android control socket failed: %s",
213 return JOB_REQUEUE_NONE
;
215 /* the original control socket is not used anymore */
216 close(this->control
);
222 if (recv(fd
, &length
, 1, 0) != 1)
224 DBG1(DBG_CFG
, "failed to read from Android control socket: %s",
226 return JOB_REQUEUE_NONE
;
230 { /* last argument */
237 case 0: /* gateway */
238 hostname
= read_argument(fd
, length
);
240 case 1: /* CA certificate name */
241 cacert
= read_argument(fd
, length
);
243 case 2: /* username */
244 username
= read_argument(fd
, length
);
246 case 3: /* password */
247 password
= read_argument(fd
, length
);
255 if (!this->creds
->add_certificate(this->creds
, cacert
))
257 DBG1(DBG_CFG
, "failed to load CA certificate");
259 /* if this is a server cert we could use the cert subject as id
260 * but we have to test first if that possible to configure */
263 gateway
= identification_create_from_string(hostname
);
264 DBG1(DBG_CFG
, "using CA certificate, gateway identitiy '%Y'", gateway
);
268 user
= identification_create_from_string(username
);
269 this->creds
->set_username_password(this->creds
, user
, password
);
272 ike_cfg
= ike_cfg_create(TRUE
, FALSE
, "0.0.0.0", IKEV2_UDP_PORT
,
273 hostname
, IKEV2_UDP_PORT
);
274 ike_cfg
->add_proposal(ike_cfg
, proposal_create_default(PROTO_IKE
));
276 peer_cfg
= peer_cfg_create("android", IKEV2
, ike_cfg
, CERT_SEND_IF_ASKED
,
277 UNIQUE_REPLACE
, 1, /* keyingtries */
278 36000, 0, /* rekey 10h, reauth none */
279 600, 600, /* jitter, over 10min */
280 TRUE
, 0, /* mobike, DPD */
281 host_create_from_string("0.0.0.0", 0) /* virt */,
282 NULL
, FALSE
, NULL
, NULL
); /* pool, mediation */
284 auth
= auth_cfg_create();
285 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_EAP
);
286 auth
->add(auth
, AUTH_RULE_IDENTITY
, user
);
287 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, TRUE
);
288 auth
= auth_cfg_create();
289 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, AUTH_CLASS_PUBKEY
);
290 auth
->add(auth
, AUTH_RULE_IDENTITY
, gateway
);
291 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, FALSE
);
293 child_cfg
= child_cfg_create("android", &lifetime
, NULL
, TRUE
, MODE_TUNNEL
,
294 ACTION_NONE
, ACTION_NONE
, ACTION_NONE
, FALSE
,
295 0, 0, NULL
, NULL
, 0);
296 child_cfg
->add_proposal(child_cfg
, proposal_create_default(PROTO_ESP
));
297 ts
= traffic_selector_create_dynamic(0, 0, 65535);
298 child_cfg
->add_traffic_selector(child_cfg
, TRUE
, ts
);
299 ts
= traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE
, "0.0.0.0",
300 0, "255.255.255.255", 65535);
301 child_cfg
->add_traffic_selector(child_cfg
, FALSE
, ts
);
302 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
);
304 /* get us an IKE_SA */
305 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
309 peer_cfg
->destroy(peer_cfg
);
310 send_status(this, VPN_ERROR_CONNECTION_FAILED
);
311 return JOB_REQUEUE_NONE
;
314 if (!ike_sa
->get_peer_cfg(ike_sa
))
316 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
318 peer_cfg
->destroy(peer_cfg
);
320 /* store the IKE_SA so we can track its progress */
321 this->ike_sa
= ike_sa
;
323 /* confirm that we received the request */
324 send_status(this, i
);
326 /* get an additional reference because initiate consumes one */
327 child_cfg
->get_ref(child_cfg
);
328 if (ike_sa
->initiate(ike_sa
, child_cfg
, 0, NULL
, NULL
) != SUCCESS
)
330 DBG1(DBG_CFG
, "failed to initiate tunnel");
331 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
333 send_status(this, VPN_ERROR_CONNECTION_FAILED
);
334 return JOB_REQUEUE_NONE
;
336 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
337 return JOB_REQUEUE_NONE
;
340 METHOD(android_service_t
, destroy
, void,
341 private_android_service_t
*this)
343 charon
->bus
->remove_listener(charon
->bus
, &this->public.listener
);
344 close(this->control
);
351 android_service_t
*android_service_create(android_creds_t
*creds
)
353 private_android_service_t
*this;
358 .ike_updown
= _ike_updown
,
359 .child_state_change
= _child_state_change
,
360 .child_updown
= _child_updown
,
361 .ike_rekey
= _ike_rekey
,
368 this->control
= android_get_control_socket("charon");
369 if (this->control
== -1)
371 DBG1(DBG_CFG
, "failed to get Android control socket");
376 if (listen(this->control
, 1) < 0)
378 DBG1(DBG_CFG
, "failed to listen on Android control socket: %s",
380 close(this->control
);
385 charon
->bus
->add_listener(charon
->bus
, &this->public.listener
);
386 this->job
= callback_job_create((callback_job_cb_t
)initiate
, this,
388 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->job
);
390 return &this->public;