0483199df5377dd70ac03ec0f2fd6377ef07cb95
[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 uint16_t local_port, remote_port = IKEV2_UDP_PORT;
81 peer_cfg_create_t peer = {
82 .cert_policy = CERT_SEND_IF_ASKED,
83 .unique = UNIQUE_REPLACE,
84 .keyingtries = 1,
85 .rekey_time = 36000, /* 10h */
86 .jitter_time = 600, /* 10min */
87 .over_time = 600, /* 10min */
88 .dpd = 30,
89 };
90
91 local_port = charon->socket->get_port(charon->socket, FALSE);
92 if (local_port != IKEV2_UDP_PORT)
93 {
94 remote_port = IKEV2_NATT_PORT;
95 }
96 ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", local_port,
97 host, remote_port, FRAGMENTATION_NO, 0);
98 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
99 ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
100 peer_cfg = peer_cfg_create(name, ike_cfg, &peer);
101 peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
102
103 return peer_cfg;
104 }
105
106 /**
107 * Add a single auth cfg of given class to peer cfg
108 */
109 static void add_auth_cfg(peer_cfg_t *peer_cfg, bool local,
110 char *id, auth_class_t class)
111 {
112 auth_cfg_t *auth;
113
114 auth = auth_cfg_create();
115 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
116 auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_string(id));
117 if (!local)
118 {
119 auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
120 }
121 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
122 }
123
124 /**
125 * Attach child config to peer config
126 */
127 static child_cfg_t* create_child_cfg(char *name)
128 {
129 child_cfg_t *child_cfg;
130 traffic_selector_t *ts;
131 child_cfg_create_t child = {
132 .lifetime = {
133 .time = {
134 .life = 10800 /* 3h */,
135 .rekey = 10200 /* 2h50min */,
136 .jitter = 300 /* 5min */
137 },
138 },
139 .mode = MODE_TUNNEL,
140 };
141
142 child_cfg = child_cfg_create(name, &child);
143 child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP,
144 "aes128gcm8-aes128gcm12-aes128gcm16-"
145 "aes256gcm8-aes256gcm12-aes256gcm16"));
146 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
147 child_cfg->add_proposal(child_cfg, proposal_create_default_aead(PROTO_ESP));
148 ts = traffic_selector_create_dynamic(0, 0, 65535);
149 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
150 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
151 "0.0.0.0", 0, "255.255.255.255", 65535);
152 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
153
154 return child_cfg;
155 }
156
157 /**
158 * Controller initiate callback
159 */
160 static bool initiate_cb(uint32_t *sa, debug_t group, level_t level,
161 ike_sa_t *ike_sa, const char *message)
162 {
163 if (ike_sa)
164 {
165 *sa = ike_sa->get_unique_id(ike_sa);
166 return FALSE;
167 }
168 return TRUE;
169 }
170
171 /**
172 * Start initiating an IKE connection
173 */
174 void start_connection(private_xpc_dispatch_t *this,
175 xpc_object_t request, xpc_object_t reply)
176 {
177 peer_cfg_t *peer_cfg;
178 child_cfg_t *child_cfg;
179 char *name, *id, *host;
180 bool success = FALSE;
181 xpc_endpoint_t endpoint;
182 xpc_connection_t channel;
183 uint32_t ike_sa;
184
185 name = (char*)xpc_dictionary_get_string(request, "name");
186 host = (char*)xpc_dictionary_get_string(request, "host");
187 id = (char*)xpc_dictionary_get_string(request, "id");
188 endpoint = xpc_dictionary_get_value(request, "channel");
189 channel = xpc_connection_create_from_endpoint(endpoint);
190
191 if (name && id && host && channel)
192 {
193 peer_cfg = create_peer_cfg(name, host);
194
195 add_auth_cfg(peer_cfg, TRUE, id, AUTH_CLASS_EAP);
196 add_auth_cfg(peer_cfg, FALSE, host, AUTH_CLASS_ANY);
197
198 child_cfg = create_child_cfg(name);
199 peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
200
201 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
202 (controller_cb_t)initiate_cb, &ike_sa, 0, FALSE) == NEED_MORE)
203 {
204 this->channels->add(this->channels, channel, ike_sa);
205 success = TRUE;
206 }
207 }
208
209 xpc_dictionary_set_bool(reply, "success", success);
210 }
211
212 /**
213 * XPC RPC command dispatch table
214 */
215 static struct {
216 char *name;
217 void (*handler)(private_xpc_dispatch_t *this,
218 xpc_object_t request, xpc_object_t reply);
219 } commands[] = {
220 { "get_version", get_version },
221 { "start_connection", start_connection },
222 };
223
224 /**
225 * Handle a received XPC request message
226 */
227 static void handle(private_xpc_dispatch_t *this, xpc_object_t request)
228 {
229 xpc_connection_t client;
230 xpc_object_t reply;
231 const char *type, *rpc;
232 bool found = FALSE;
233 int i;
234
235 type = xpc_dictionary_get_string(request, "type");
236 if (type)
237 {
238 if (streq(type, "rpc"))
239 {
240 reply = xpc_dictionary_create_reply(request);
241 rpc = xpc_dictionary_get_string(request, "rpc");
242 if (reply && rpc)
243 {
244 for (i = 0; i < countof(commands); i++)
245 {
246 if (streq(commands[i].name, rpc))
247 {
248 found = TRUE;
249 commands[i].handler(this, request, reply);
250 break;
251 }
252 }
253 }
254 if (!found)
255 {
256 DBG1(DBG_CFG, "received invalid XPC rpc command: %s", rpc);
257 }
258 if (reply)
259 {
260 client = xpc_dictionary_get_remote_connection(request);
261 xpc_connection_send_message(client, reply);
262 xpc_release(reply);
263 }
264 }
265 else
266 {
267 DBG1(DBG_CFG, "received unknown XPC message type: %s", type);
268 }
269 }
270 else
271 {
272 DBG1(DBG_CFG, "received XPC message without a type");
273 }
274 }
275
276 /**
277 * Finalizer for client connections
278 */
279 static void cleanup_connection(private_xpc_dispatch_t *this)
280 {
281 if (ref_put(&this->refcount))
282 {
283 DBG1(DBG_CFG, "no XPC connections, raising SIGTERM");
284 kill(this->pid, SIGTERM);
285 }
286 }
287
288 /**
289 * Set up GCD handler for XPC events
290 */
291 static void set_handler(private_xpc_dispatch_t *this)
292 {
293 xpc_retain(this->service);
294 xpc_connection_set_event_handler(this->service, ^(xpc_object_t conn)
295 {
296 xpc_connection_set_event_handler(conn, ^(xpc_object_t event)
297 {
298 if (xpc_get_type(event) == XPC_TYPE_DICTIONARY)
299 {
300 handle(this, event);
301 }
302 });
303 ref_get(&this->refcount);
304 xpc_connection_set_context(conn, this);
305 xpc_connection_set_finalizer_f(conn, (void*)cleanup_connection);
306 xpc_connection_resume(conn);
307 });
308 xpc_connection_resume(this->service);
309 }
310
311 METHOD(xpc_dispatch_t, destroy, void,
312 private_xpc_dispatch_t *this)
313 {
314 charon->bus->remove_listener(charon->bus, &this->channels->listener);
315 this->channels->destroy(this->channels);
316 if (this->service)
317 {
318 xpc_connection_suspend(this->service);
319 xpc_release(this->service);
320 }
321 free(this);
322 }
323
324 /**
325 * See header
326 */
327 xpc_dispatch_t *xpc_dispatch_create()
328 {
329 private_xpc_dispatch_t *this;
330
331 INIT(this,
332 .public = {
333 .destroy = _destroy,
334 },
335 .channels = xpc_channels_create(),
336 .queue = dispatch_queue_create("org.strongswan.charon-xpc.q",
337 DISPATCH_QUEUE_CONCURRENT),
338 .pid = getpid(),
339 );
340 charon->bus->add_listener(charon->bus, &this->channels->listener);
341
342 this->service = xpc_connection_create_mach_service(
343 "org.strongswan.charon-xpc", this->queue,
344 XPC_CONNECTION_MACH_SERVICE_LISTENER);
345 if (!this->service)
346 {
347 destroy(this);
348 return NULL;
349 }
350
351 set_handler(this);
352
353 return &this->public;
354 }