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