Initiate consumes a child_sa reference, so get an additional one.
[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 * listener to track progress
40 */
41 listener_t listener;
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 * Read a string argument from the Android control socket
76 */
77 static char *read_argument(int fd, u_char length)
78 {
79 int offset = 0;
80 char *data = malloc(length + 1);
81 while (offset < length)
82 {
83 int n = recv(fd, &data[offset], length - offset, 0);
84 if (n < 0)
85 {
86 DBG1(DBG_CFG, "failed to read argument from Android"
87 " control socket: %s", strerror(errno));
88 free(data);
89 return NULL;
90 }
91 offset += n;
92 }
93 data[length] = '\0';
94 DBG1(DBG_CFG, "received argument from Android control socket: %s", data);
95 return data;
96 }
97
98 /**
99 * handle the request received from the Android control socket
100 */
101 static job_requeue_t initiate(private_android_service_t *this)
102 {
103 bool oldstate;
104 int fd, i = 0;
105 char *hostname = NULL, *cacert = NULL, *username = NULL, *password = NULL;
106 identification_t *gateway = NULL, *user = NULL;
107 ike_cfg_t *ike_cfg;
108 peer_cfg_t *peer_cfg;
109 child_cfg_t *child_cfg;
110 traffic_selector_t *ts;
111 ike_sa_t *ike_sa;
112 auth_cfg_t *auth;
113 lifetime_cfg_t lifetime = {
114 .time = {
115 .life = 10800, /* 3h */
116 .rekey = 10200, /* 2h50min */
117 .jitter = 300 /* 5min */
118 }
119 };
120
121 fd = accept(this->control, NULL, 0);
122 if (fd < 0)
123 {
124 DBG1(DBG_CFG, "accept on Android control socket failed: %s",
125 strerror(errno));
126 return JOB_REQUEUE_NONE;
127 }
128 close(this->control);
129
130 while (TRUE)
131 {
132 u_char length;
133 if (recv(fd, &length, 1, 0) != 1)
134 {
135 DBG1(DBG_CFG, "failed to read from Android control socket: %s",
136 strerror(errno));
137 return JOB_REQUEUE_NONE;
138 }
139
140 if (length == 0xFF)
141 { /* last argument */
142 break;
143 }
144 else
145 {
146 switch (i++)
147 {
148 case 0: /* gateway */
149 hostname = read_argument(fd, length);
150 break;
151 case 1: /* CA certificate name */
152 cacert = read_argument(fd, length);
153 break;
154 case 2: /* username */
155 username = read_argument(fd, length);
156 break;
157 case 3: /* password */
158 password = read_argument(fd, length);
159 break;
160 }
161 }
162 }
163
164 if (cacert)
165 {
166 if (!this->creds->add_certificate(this->creds, cacert))
167 {
168 DBG1(DBG_CFG, "failed to load CA certificate");
169 }
170 /* if this is a server cert we could use the cert subject as id
171 * but we have to test first if that possible to configure */
172 }
173
174 gateway = identification_create_from_string(hostname);
175 DBG1(DBG_CFG, "using CA certificate, gateway identitiy '%Y'", gateway);
176
177 if (username)
178 {
179 user = identification_create_from_string(username);
180 this->creds->set_username_password(this->creds, user, password);
181 }
182
183 ike_cfg = ike_cfg_create(TRUE, FALSE, "0.0.0.0", IKEV2_UDP_PORT,
184 hostname, IKEV2_UDP_PORT);
185 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
186
187 peer_cfg = peer_cfg_create("android", 2, ike_cfg, CERT_SEND_IF_ASKED,
188 UNIQUE_REPLACE, 1, /* keyingtries */
189 36000, 0, /* rekey 10h, reauth none */
190 600, 600, /* jitter, over 10min */
191 TRUE, 0, /* mobike, DPD */
192 host_create_from_string("0.0.0.0", 0) /* virt */,
193 NULL, FALSE, NULL, NULL); /* pool, mediation */
194
195 auth = auth_cfg_create();
196 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
197 auth->add(auth, AUTH_RULE_IDENTITY, user);
198 peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
199 auth = auth_cfg_create();
200 auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
201 auth->add(auth, AUTH_RULE_IDENTITY, gateway);
202 peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
203
204 child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL,
205 ACTION_NONE, ACTION_NONE, FALSE, 0, 0);
206 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
207 ts = traffic_selector_create_dynamic(0, 0, 65535);
208 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
209 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
210 0, "255.255.255.255", 65535);
211 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
212 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
213 /* get an additional reference because initiate consumes one */
214 child_cfg->get_ref(child_cfg);
215
216 /*this->listener.ike_up_down = ike_up_down;
217 this->listener.child_up_down = child_up_down;
218 charon->bus->add_listener(charon->bus, &this->listener);*/
219
220 /* confirm that we received the request */
221 u_char code = i;
222 send(fd, &code, 1, 0);
223
224 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
225 controller_cb_empty, NULL) != SUCCESS)
226 {
227 DBG1(DBG_CFG, "failed to initiate tunnel");
228 code = VPN_ERROR_CONNECTION_FAILED;
229 send(fd, &code, 1, 0);
230 return JOB_REQUEUE_NONE;
231 }
232 property_set("vpn.status", "ok");
233 return JOB_REQUEUE_NONE;
234 }
235
236 METHOD(android_service_t, destroy, void,
237 private_android_service_t *this)
238 {
239 free(this);
240 }
241
242 /**
243 * See header
244 */
245 android_service_t *android_service_create(android_creds_t *creds)
246 {
247 private_android_service_t *this;
248
249 INIT(this,
250 .public = {
251 .destroy = _destroy,
252 },
253 .creds = creds,
254 );
255
256 this->control = android_get_control_socket("charon");
257 if (this->control == -1)
258 {
259 DBG1(DBG_CFG, "failed to get Android control socket");
260 free(this);
261 return NULL;
262 }
263
264 if (listen(this->control, 1) < 0)
265 {
266 DBG1(DBG_CFG, "failed to listen on Android control socket: %s",
267 strerror(errno));
268 close(this->control);
269 free(this);
270 return NULL;
271 }
272
273 this->job = callback_job_create((callback_job_cb_t)initiate, this,
274 NULL, NULL);
275 charon->processor->queue_job(charon->processor, (job_t*)this->job);
276
277 return &this->public;
278 }
279