charon-cmd: use a copy of pid in initiate callback
[strongswan.git] / src / charon-cmd / cmd / cmd_connection.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2013 Martin Willi
6 * Copyright (C) 2013 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "cmd_connection.h"
20
21 #include <signal.h>
22 #include <unistd.h>
23
24 #include <utils/debug.h>
25 #include <processing/jobs/callback_job.h>
26 #include <threading/thread.h>
27 #include <daemon.h>
28
29 typedef enum profile_t profile_t;
30 typedef struct private_cmd_connection_t private_cmd_connection_t;
31
32 /**
33 * Connection profiles we support
34 */
35 enum profile_t {
36 PROF_UNDEF,
37 PROF_V2_PUB,
38 PROF_V2_EAP,
39 PROF_V2_PUB_EAP,
40 PROF_V1_PUB,
41 PROF_V1_PUB_AM,
42 PROF_V1_XAUTH,
43 PROF_V1_XAUTH_AM,
44 PROF_V1_XAUTH_PSK,
45 PROF_V1_XAUTH_PSK_AM,
46 PROF_V1_HYBRID,
47 PROF_V1_HYBRID_AM,
48 };
49
50 ENUM(profile_names, PROF_V2_PUB, PROF_V1_HYBRID_AM,
51 "ikev2-pub",
52 "ikev2-eap",
53 "ikev2-pub-eap",
54 "ikev1-pub",
55 "ikev1-pub-am",
56 "ikev1-xauth",
57 "ikev1-xauth-am",
58 "ikev1-xauth-psk",
59 "ikev1-xauth-psk-am",
60 "ikev1-hybrid",
61 "ikev1-hybrid-am",
62 );
63
64 /**
65 * Private data of an cmd_connection_t object.
66 */
67 struct private_cmd_connection_t {
68
69 /**
70 * Public cmd_connection_t interface.
71 */
72 cmd_connection_t public;
73
74 /**
75 * Process ID to terminate on failure
76 */
77 pid_t pid;
78
79 /**
80 * List of local traffic selectors
81 */
82 linked_list_t *local_ts;
83
84 /**
85 * List of remote traffic selectors
86 */
87 linked_list_t *remote_ts;
88
89 /**
90 * Hostname to connect to
91 */
92 char *host;
93
94 /**
95 * Server identity, or NULL to use host
96 */
97 char *server;
98
99 /**
100 * Local identity
101 */
102 char *identity;
103
104 /**
105 * Is a private key configured
106 */
107 bool key_seen;
108
109 /**
110 * Selected connection profile
111 */
112 profile_t profile;
113 };
114
115 /**
116 * Shut down application
117 */
118 static void terminate(pid_t pid)
119 {
120 kill(pid, SIGUSR1);
121 }
122
123 /**
124 * Create peer config with associated ike config
125 */
126 static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
127 {
128 ike_cfg_t *ike_cfg;
129 peer_cfg_t *peer_cfg;
130 u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
131 ike_version_t version = IKE_ANY;
132 bool aggressive = FALSE;
133
134 switch (this->profile)
135 {
136 case PROF_UNDEF:
137 case PROF_V2_PUB:
138 case PROF_V2_EAP:
139 case PROF_V2_PUB_EAP:
140 version = IKEV2;
141 break;
142 case PROF_V1_PUB_AM:
143 case PROF_V1_XAUTH_AM:
144 case PROF_V1_XAUTH_PSK_AM:
145 case PROF_V1_HYBRID_AM:
146 aggressive = TRUE;
147 /* FALL */
148 case PROF_V1_PUB:
149 case PROF_V1_XAUTH:
150 case PROF_V1_XAUTH_PSK:
151 case PROF_V1_HYBRID:
152 version = IKEV1;
153 break;
154 }
155
156 local_port = charon->socket->get_port(charon->socket, FALSE);
157 if (local_port != IKEV2_UDP_PORT)
158 {
159 remote_port = IKEV2_NATT_PORT;
160 }
161 ike_cfg = ike_cfg_create(version, TRUE, FALSE, "0.0.0.0", FALSE, local_port,
162 this->host, FALSE, remote_port, FRAGMENTATION_NO, 0);
163 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
164 peer_cfg = peer_cfg_create("cmd", ike_cfg,
165 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
166 36000, 0, /* rekey 10h, reauth none */
167 600, 600, /* jitter, over 10min */
168 TRUE, aggressive, /* mobike, aggressive */
169 30, 0, /* DPD delay, timeout */
170 FALSE, NULL, NULL); /* mediation */
171 peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
172
173 return peer_cfg;
174 }
175
176 /**
177 * Add a single auth cfg of given class to peer cfg
178 */
179 static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
180 bool local, auth_class_t class)
181 {
182 identification_t *id;
183 auth_cfg_t *auth;
184
185 auth = auth_cfg_create();
186 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
187 if (local)
188 {
189 id = identification_create_from_string(this->identity);
190 }
191 else
192 {
193 if (this->server)
194 {
195 id = identification_create_from_string(this->server);
196 }
197 else
198 {
199 id = identification_create_from_string(this->host);
200 }
201 auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
202 }
203 auth->add(auth, AUTH_RULE_IDENTITY, id);
204 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
205 }
206
207 /**
208 * Attach authentication configs to peer config
209 */
210 static bool add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
211 {
212 if (this->profile == PROF_UNDEF)
213 {
214 if (this->key_seen)
215 {
216 this->profile = PROF_V2_PUB;
217 }
218 else
219 {
220 this->profile = PROF_V2_EAP;
221 }
222 }
223 switch (this->profile)
224 {
225 case PROF_V2_PUB:
226 case PROF_V2_PUB_EAP:
227 case PROF_V1_PUB:
228 case PROF_V1_XAUTH:
229 case PROF_V1_PUB_AM:
230 case PROF_V1_XAUTH_AM:
231 if (!this->key_seen)
232 {
233 DBG1(DBG_CFG, "missing private key for profile %N",
234 profile_names, this->profile);
235 return FALSE;
236 }
237 break;
238 default:
239 break;
240 }
241
242 switch (this->profile)
243 {
244 case PROF_V2_PUB:
245 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
246 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
247 break;
248 case PROF_V2_EAP:
249 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
250 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
251 break;
252 case PROF_V2_PUB_EAP:
253 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
254 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
255 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
256 break;
257 case PROF_V1_PUB:
258 case PROF_V1_PUB_AM:
259 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
260 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
261 break;
262 case PROF_V1_XAUTH:
263 case PROF_V1_XAUTH_AM:
264 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
265 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
266 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
267 break;
268 case PROF_V1_XAUTH_PSK:
269 case PROF_V1_XAUTH_PSK_AM:
270 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PSK);
271 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
272 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PSK);
273 break;
274 case PROF_V1_HYBRID:
275 case PROF_V1_HYBRID_AM:
276 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
277 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
278 break;
279 default:
280 return FALSE;
281 }
282 return TRUE;
283 }
284
285 /**
286 * Attach child config to peer config
287 */
288 static child_cfg_t* create_child_cfg(private_cmd_connection_t *this)
289 {
290 child_cfg_t *child_cfg;
291 traffic_selector_t *ts;
292 lifetime_cfg_t lifetime = {
293 .time = {
294 .life = 10800 /* 3h */,
295 .rekey = 10200 /* 2h50min */,
296 .jitter = 300 /* 5min */
297 }
298 };
299
300 child_cfg = child_cfg_create("cmd", &lifetime,
301 NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
302 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
303 0, 0, NULL, NULL, 0);
304 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
305 while (this->local_ts->remove_first(this->local_ts, (void**)&ts) == SUCCESS)
306 {
307 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
308 }
309 if (this->remote_ts->get_count(this->remote_ts) == 0)
310 {
311 /* add a 0.0.0.0/0 TS for remote side if none given */
312 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
313 "0.0.0.0", 0, "255.255.255.255", 65535);
314 this->remote_ts->insert_last(this->remote_ts, ts);
315 }
316 while (this->remote_ts->remove_first(this->remote_ts,
317 (void**)&ts) == SUCCESS)
318 {
319 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
320 }
321
322 return child_cfg;
323 }
324
325 /**
326 * Initiate the configured connection
327 */
328 static job_requeue_t initiate(private_cmd_connection_t *this)
329 {
330 peer_cfg_t *peer_cfg;
331 child_cfg_t *child_cfg;
332 pid_t pid = this->pid;
333
334 if (!this->host)
335 {
336 DBG1(DBG_CFG, "unable to initiate, missing --host option");
337 terminate(pid);
338 return JOB_REQUEUE_NONE;
339 }
340 if (!this->identity)
341 {
342 DBG1(DBG_CFG, "unable to initiate, missing --identity option");
343 terminate(pid);
344 return JOB_REQUEUE_NONE;
345 }
346
347 peer_cfg = create_peer_cfg(this);
348
349 if (!add_auth_cfgs(this, peer_cfg))
350 {
351 peer_cfg->destroy(peer_cfg);
352 terminate(pid);
353 return JOB_REQUEUE_NONE;
354 }
355
356 child_cfg = create_child_cfg(this);
357 peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
358
359 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
360 controller_cb_empty, NULL, 0) != SUCCESS)
361 {
362 terminate(pid);
363 }
364 return JOB_REQUEUE_NONE;
365 }
366
367 /**
368 * Create a traffic selector from string, add to list
369 */
370 static void add_ts(private_cmd_connection_t *this,
371 linked_list_t *list, char *string)
372 {
373 traffic_selector_t *ts;
374
375 ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
376 if (!ts)
377 {
378 DBG1(DBG_CFG, "invalid traffic selector: %s", string);
379 exit(1);
380 }
381 list->insert_last(list, ts);
382 }
383
384 /**
385 * Parse profile name identifier
386 */
387 static void set_profile(private_cmd_connection_t *this, char *name)
388 {
389 int profile;
390
391 profile = enum_from_name(profile_names, name);
392 if (profile == -1)
393 {
394 DBG1(DBG_CFG, "unknown connection profile: %s", name);
395 exit(1);
396 }
397 this->profile = profile;
398 }
399
400 METHOD(cmd_connection_t, handle, bool,
401 private_cmd_connection_t *this, cmd_option_type_t opt, char *arg)
402 {
403 switch (opt)
404 {
405 case CMD_OPT_HOST:
406 this->host = arg;
407 break;
408 case CMD_OPT_REMOTE_IDENTITY:
409 this->server = arg;
410 break;
411 case CMD_OPT_IDENTITY:
412 this->identity = arg;
413 break;
414 case CMD_OPT_RSA:
415 case CMD_OPT_AGENT:
416 case CMD_OPT_PKCS12:
417 this->key_seen = TRUE;
418 break;
419 case CMD_OPT_LOCAL_TS:
420 add_ts(this, this->local_ts, arg);
421 break;
422 case CMD_OPT_REMOTE_TS:
423 add_ts(this, this->remote_ts, arg);
424 break;
425 case CMD_OPT_PROFILE:
426 set_profile(this, arg);
427 break;
428 default:
429 return FALSE;
430 }
431 return TRUE;
432 }
433
434 METHOD(cmd_connection_t, destroy, void,
435 private_cmd_connection_t *this)
436 {
437 this->local_ts->destroy_offset(this->local_ts,
438 offsetof(traffic_selector_t, destroy));
439 this->remote_ts->destroy_offset(this->remote_ts,
440 offsetof(traffic_selector_t, destroy));
441 free(this);
442 }
443
444 /**
445 * See header
446 */
447 cmd_connection_t *cmd_connection_create()
448 {
449 private_cmd_connection_t *this;
450
451 INIT(this,
452 .public = {
453 .handle = _handle,
454 .destroy = _destroy,
455 },
456 .pid = getpid(),
457 .local_ts = linked_list_create(),
458 .remote_ts = linked_list_create(),
459 .profile = PROF_UNDEF,
460 );
461
462 /* always include the virtual IP in traffic selector list */
463 this->local_ts->insert_last(this->local_ts,
464 traffic_selector_create_dynamic(0, 0, 65535));
465
466 /* queue job, gets initiated as soon as we are up and running */
467 lib->processor->queue_job(lib->processor,
468 (job_t*)callback_job_create_with_prio(
469 (callback_job_cb_t)initiate, this, NULL,
470 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
471
472 return &this->public;
473 }