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