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