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