ike: Add an additional but separate AEAD proposal to IKE config, if supported
[strongswan.git] / src / frontends / osx / charon-xpc / xpc_dispatch.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
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 "xpc_dispatch.h"
17 #include "xpc_channels.h"
18
19 #include <xpc/xpc.h>
20 #include <signal.h>
21 #include <unistd.h>
22
23 #include <daemon.h>
24 #include <processing/jobs/callback_job.h>
25
26 typedef struct private_xpc_dispatch_t private_xpc_dispatch_t;
27
28 /**
29 * Private data of an xpc_dispatch_t object.
30 */
31 struct private_xpc_dispatch_t {
32
33 /**
34 * Public xpc_dispatch_t interface.
35 */
36 xpc_dispatch_t public;
37
38 /**
39 * XPC service we offer
40 */
41 xpc_connection_t service;
42
43 /**
44 * XPC IKE_SA specific channels to App
45 */
46 xpc_channels_t *channels;
47
48 /**
49 * GCD queue for XPC events
50 */
51 dispatch_queue_t queue;
52
53 /**
54 * Number of active App connections
55 */
56 refcount_t refcount;
57
58 /**
59 * PID of main thread
60 */
61 pid_t pid;
62 };
63
64 /**
65 * Return version of this helper
66 */
67 static void get_version(private_xpc_dispatch_t *this,
68 xpc_object_t request, xpc_object_t reply)
69 {
70 xpc_dictionary_set_string(reply, "version", PACKAGE_VERSION);
71 }
72
73 /**
74 * Create peer config with associated ike config
75 */
76 static peer_cfg_t* create_peer_cfg(char *name, char *host)
77 {
78 ike_cfg_t *ike_cfg;
79 peer_cfg_t *peer_cfg;
80 u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
81
82 local_port = charon->socket->get_port(charon->socket, FALSE);
83 if (local_port != IKEV2_UDP_PORT)
84 {
85 remote_port = IKEV2_NATT_PORT;
86 }
87 ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", local_port,
88 host, remote_port, FRAGMENTATION_NO, 0);
89 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
90 ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
91 peer_cfg = peer_cfg_create(name, ike_cfg,
92 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
93 36000, 0, /* rekey 10h, reauth none */
94 600, 600, /* jitter, over 10min */
95 TRUE, FALSE, TRUE, /* mobike, aggressive, pull */
96 30, 0, /* DPD delay, timeout */
97 FALSE, NULL, NULL); /* mediation */
98 peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
99
100 return peer_cfg;
101 }
102
103 /**
104 * Add a single auth cfg of given class to peer cfg
105 */
106 static void add_auth_cfg(peer_cfg_t *peer_cfg, bool local,
107 char *id, auth_class_t class)
108 {
109 auth_cfg_t *auth;
110
111 auth = auth_cfg_create();
112 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
113 auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_string(id));
114 if (!local)
115 {
116 auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
117 }
118 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
119 }
120
121 /**
122 * Attach child config to peer config
123 */
124 static child_cfg_t* create_child_cfg(char *name)
125 {
126 child_cfg_t *child_cfg;
127 traffic_selector_t *ts;
128 lifetime_cfg_t lifetime = {
129 .time = {
130 .life = 10800 /* 3h */,
131 .rekey = 10200 /* 2h50min */,
132 .jitter = 300 /* 5min */
133 }
134 };
135
136 child_cfg = child_cfg_create(name, &lifetime,
137 NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
138 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
139 0, 0, NULL, NULL, 0);
140 child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP,
141 "aes128gcm8-aes128gcm12-aes128gcm16-"
142 "aes256gcm8-aes256gcm12-aes256gcm16"));
143 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
144 ts = traffic_selector_create_dynamic(0, 0, 65535);
145 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
146 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
147 "0.0.0.0", 0, "255.255.255.255", 65535);
148 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
149
150 return child_cfg;
151 }
152
153 /**
154 * Controller initiate callback
155 */
156 static bool initiate_cb(u_int32_t *sa, debug_t group, level_t level,
157 ike_sa_t *ike_sa, const char *message)
158 {
159 if (ike_sa)
160 {
161 *sa = ike_sa->get_unique_id(ike_sa);
162 return FALSE;
163 }
164 return TRUE;
165 }
166
167 /**
168 * Start initiating an IKE connection
169 */
170 void start_connection(private_xpc_dispatch_t *this,
171 xpc_object_t request, xpc_object_t reply)
172 {
173 peer_cfg_t *peer_cfg;
174 child_cfg_t *child_cfg;
175 char *name, *id, *host;
176 bool success = FALSE;
177 xpc_endpoint_t endpoint;
178 xpc_connection_t channel;
179 u_int32_t ike_sa;
180
181 name = (char*)xpc_dictionary_get_string(request, "name");
182 host = (char*)xpc_dictionary_get_string(request, "host");
183 id = (char*)xpc_dictionary_get_string(request, "id");
184 endpoint = xpc_dictionary_get_value(request, "channel");
185 channel = xpc_connection_create_from_endpoint(endpoint);
186
187 if (name && id && host && channel)
188 {
189 peer_cfg = create_peer_cfg(name, host);
190
191 add_auth_cfg(peer_cfg, TRUE, id, AUTH_CLASS_EAP);
192 add_auth_cfg(peer_cfg, FALSE, host, AUTH_CLASS_ANY);
193
194 child_cfg = create_child_cfg(name);
195 peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
196
197 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
198 (controller_cb_t)initiate_cb, &ike_sa, 0) == NEED_MORE)
199 {
200 this->channels->add(this->channels, channel, ike_sa);
201 success = TRUE;
202 }
203 }
204
205 xpc_dictionary_set_bool(reply, "success", success);
206 }
207
208 /**
209 * XPC RPC command dispatch table
210 */
211 static struct {
212 char *name;
213 void (*handler)(private_xpc_dispatch_t *this,
214 xpc_object_t request, xpc_object_t reply);
215 } commands[] = {
216 { "get_version", get_version },
217 { "start_connection", start_connection },
218 };
219
220 /**
221 * Handle a received XPC request message
222 */
223 static void handle(private_xpc_dispatch_t *this, xpc_object_t request)
224 {
225 xpc_connection_t client;
226 xpc_object_t reply;
227 const char *type, *rpc;
228 bool found = FALSE;
229 int i;
230
231 type = xpc_dictionary_get_string(request, "type");
232 if (type)
233 {
234 if (streq(type, "rpc"))
235 {
236 reply = xpc_dictionary_create_reply(request);
237 rpc = xpc_dictionary_get_string(request, "rpc");
238 if (reply && rpc)
239 {
240 for (i = 0; i < countof(commands); i++)
241 {
242 if (streq(commands[i].name, rpc))
243 {
244 found = TRUE;
245 commands[i].handler(this, request, reply);
246 break;
247 }
248 }
249 }
250 if (!found)
251 {
252 DBG1(DBG_CFG, "received invalid XPC rpc command: %s", rpc);
253 }
254 if (reply)
255 {
256 client = xpc_dictionary_get_remote_connection(request);
257 xpc_connection_send_message(client, reply);
258 xpc_release(reply);
259 }
260 }
261 else
262 {
263 DBG1(DBG_CFG, "received unknown XPC message type: %s", type);
264 }
265 }
266 else
267 {
268 DBG1(DBG_CFG, "received XPC message without a type");
269 }
270 }
271
272 /**
273 * Finalizer for client connections
274 */
275 static void cleanup_connection(private_xpc_dispatch_t *this)
276 {
277 if (ref_put(&this->refcount))
278 {
279 DBG1(DBG_CFG, "no XPC connections, raising SIGTERM");
280 kill(this->pid, SIGTERM);
281 }
282 }
283
284 /**
285 * Set up GCD handler for XPC events
286 */
287 static void set_handler(private_xpc_dispatch_t *this)
288 {
289 xpc_retain(this->service);
290 xpc_connection_set_event_handler(this->service, ^(xpc_object_t conn)
291 {
292 xpc_connection_set_event_handler(conn, ^(xpc_object_t event)
293 {
294 if (xpc_get_type(event) == XPC_TYPE_DICTIONARY)
295 {
296 handle(this, event);
297 }
298 });
299 ref_get(&this->refcount);
300 xpc_connection_set_context(conn, this);
301 xpc_connection_set_finalizer_f(conn, (void*)cleanup_connection);
302 xpc_connection_resume(conn);
303 });
304 xpc_connection_resume(this->service);
305 }
306
307 METHOD(xpc_dispatch_t, destroy, void,
308 private_xpc_dispatch_t *this)
309 {
310 charon->bus->remove_listener(charon->bus, &this->channels->listener);
311 this->channels->destroy(this->channels);
312 if (this->service)
313 {
314 xpc_connection_suspend(this->service);
315 xpc_release(this->service);
316 }
317 free(this);
318 }
319
320 /**
321 * See header
322 */
323 xpc_dispatch_t *xpc_dispatch_create()
324 {
325 private_xpc_dispatch_t *this;
326
327 INIT(this,
328 .public = {
329 .destroy = _destroy,
330 },
331 .channels = xpc_channels_create(),
332 .queue = dispatch_queue_create("org.strongswan.charon-xpc.q",
333 DISPATCH_QUEUE_CONCURRENT),
334 .pid = getpid(),
335 );
336 charon->bus->add_listener(charon->bus, &this->channels->listener);
337
338 this->service = xpc_connection_create_mach_service(
339 "org.strongswan.charon-xpc", this->queue,
340 XPC_CONNECTION_MACH_SERVICE_LISTENER);
341 if (!this->service)
342 {
343 destroy(this);
344 return NULL;
345 }
346
347 set_handler(this);
348
349 return &this->public;
350 }