charon-cmd: Add an --ike-proposal option to specify non-default IKE proposals
[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 * List of IKE proposals
91 */
92 linked_list_t *ike_proposals;
93
94 /**
95 * Hostname to connect to
96 */
97 char *host;
98
99 /**
100 * Server identity, or NULL to use host
101 */
102 char *server;
103
104 /**
105 * Local identity
106 */
107 char *identity;
108
109 /**
110 * XAuth/EAP identity
111 */
112 char *xautheap;
113
114 /**
115 * Is a private key configured
116 */
117 bool key_seen;
118
119 /**
120 * Selected connection profile
121 */
122 profile_t profile;
123 };
124
125 /**
126 * Shut down application
127 */
128 static void terminate(pid_t pid)
129 {
130 kill(pid, SIGUSR1);
131 }
132
133 /**
134 * Create peer config with associated ike config
135 */
136 static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
137 {
138 ike_cfg_t *ike_cfg;
139 peer_cfg_t *peer_cfg;
140 u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
141 ike_version_t version = IKE_ANY;
142 bool aggressive = FALSE;
143 proposal_t *proposal;
144
145 switch (this->profile)
146 {
147 case PROF_UNDEF:
148 case PROF_V2_PUB:
149 case PROF_V2_EAP:
150 case PROF_V2_PUB_EAP:
151 version = IKEV2;
152 break;
153 case PROF_V1_PUB_AM:
154 case PROF_V1_XAUTH_AM:
155 case PROF_V1_XAUTH_PSK_AM:
156 case PROF_V1_HYBRID_AM:
157 aggressive = TRUE;
158 /* FALL */
159 case PROF_V1_PUB:
160 case PROF_V1_XAUTH:
161 case PROF_V1_XAUTH_PSK:
162 case PROF_V1_HYBRID:
163 version = IKEV1;
164 break;
165 }
166
167 local_port = charon->socket->get_port(charon->socket, FALSE);
168 if (local_port != IKEV2_UDP_PORT)
169 {
170 remote_port = IKEV2_NATT_PORT;
171 }
172 ike_cfg = ike_cfg_create(version, TRUE, FALSE, "0.0.0.0", local_port,
173 this->host, remote_port, FRAGMENTATION_NO, 0);
174 if (this->ike_proposals->get_count(this->ike_proposals))
175 {
176 while (this->ike_proposals->remove_first(this->ike_proposals,
177 (void**)&proposal) == SUCCESS)
178 {
179 ike_cfg->add_proposal(ike_cfg, proposal);
180 }
181 }
182 else
183 {
184 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
185 }
186 peer_cfg = peer_cfg_create("cmd", ike_cfg,
187 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
188 36000, 0, /* rekey 10h, reauth none */
189 600, 600, /* jitter, over 10min */
190 TRUE, aggressive, TRUE, /* mobike, aggressive, pull */
191 30, 0, /* DPD delay, timeout */
192 FALSE, NULL, NULL); /* mediation */
193 peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
194
195 return peer_cfg;
196 }
197
198 /**
199 * Add a single auth cfg of given class to peer cfg
200 */
201 static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
202 bool local, auth_class_t class)
203 {
204 identification_t *id;
205 auth_cfg_t *auth;
206
207 auth = auth_cfg_create();
208 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
209 if (local)
210 {
211 id = identification_create_from_string(this->identity);
212 if (this->xautheap)
213 {
214 switch (class)
215 {
216 case AUTH_CLASS_EAP:
217 auth->add(auth, AUTH_RULE_EAP_IDENTITY,
218 identification_create_from_string(this->xautheap));
219 break;
220 case AUTH_CLASS_XAUTH:
221 auth->add(auth, AUTH_RULE_XAUTH_IDENTITY,
222 identification_create_from_string(this->xautheap));
223 break;
224 default:
225 break;
226 }
227 }
228 }
229 else
230 {
231 if (this->server)
232 {
233 id = identification_create_from_string(this->server);
234 }
235 else
236 {
237 id = identification_create_from_string(this->host);
238 }
239 auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
240 }
241 auth->add(auth, AUTH_RULE_IDENTITY, id);
242 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
243 }
244
245 /**
246 * Attach authentication configs to peer config
247 */
248 static bool add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
249 {
250 if (this->profile == PROF_UNDEF)
251 {
252 if (this->key_seen)
253 {
254 this->profile = PROF_V2_PUB;
255 }
256 else
257 {
258 this->profile = PROF_V2_EAP;
259 }
260 }
261 switch (this->profile)
262 {
263 case PROF_V2_PUB:
264 case PROF_V2_PUB_EAP:
265 case PROF_V1_PUB:
266 case PROF_V1_XAUTH:
267 case PROF_V1_PUB_AM:
268 case PROF_V1_XAUTH_AM:
269 if (!this->key_seen)
270 {
271 DBG1(DBG_CFG, "missing private key for profile %N",
272 profile_names, this->profile);
273 return FALSE;
274 }
275 break;
276 default:
277 break;
278 }
279
280 switch (this->profile)
281 {
282 case PROF_V2_PUB:
283 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
284 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
285 break;
286 case PROF_V2_EAP:
287 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
288 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
289 break;
290 case PROF_V2_PUB_EAP:
291 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
292 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
293 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
294 break;
295 case PROF_V1_PUB:
296 case PROF_V1_PUB_AM:
297 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
298 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
299 break;
300 case PROF_V1_XAUTH:
301 case PROF_V1_XAUTH_AM:
302 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
303 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
304 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
305 break;
306 case PROF_V1_XAUTH_PSK:
307 case PROF_V1_XAUTH_PSK_AM:
308 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PSK);
309 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
310 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PSK);
311 break;
312 case PROF_V1_HYBRID:
313 case PROF_V1_HYBRID_AM:
314 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
315 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
316 break;
317 default:
318 return FALSE;
319 }
320 return TRUE;
321 }
322
323 /**
324 * Attach child config to peer config
325 */
326 static child_cfg_t* create_child_cfg(private_cmd_connection_t *this)
327 {
328 child_cfg_t *child_cfg;
329 traffic_selector_t *ts;
330 lifetime_cfg_t lifetime = {
331 .time = {
332 .life = 10800 /* 3h */,
333 .rekey = 10200 /* 2h50min */,
334 .jitter = 300 /* 5min */
335 }
336 };
337
338 child_cfg = child_cfg_create("cmd", &lifetime,
339 NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
340 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
341 0, 0, NULL, NULL, 0);
342 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
343 while (this->local_ts->remove_first(this->local_ts, (void**)&ts) == SUCCESS)
344 {
345 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
346 }
347 if (this->remote_ts->get_count(this->remote_ts) == 0)
348 {
349 /* add a 0.0.0.0/0 TS for remote side if none given */
350 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
351 "0.0.0.0", 0, "255.255.255.255", 65535);
352 this->remote_ts->insert_last(this->remote_ts, ts);
353 }
354 while (this->remote_ts->remove_first(this->remote_ts,
355 (void**)&ts) == SUCCESS)
356 {
357 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
358 }
359
360 return child_cfg;
361 }
362
363 /**
364 * Initiate the configured connection
365 */
366 static job_requeue_t initiate(private_cmd_connection_t *this)
367 {
368 peer_cfg_t *peer_cfg;
369 child_cfg_t *child_cfg;
370 pid_t pid = this->pid;
371
372 if (!this->host)
373 {
374 DBG1(DBG_CFG, "unable to initiate, missing --host option");
375 terminate(pid);
376 return JOB_REQUEUE_NONE;
377 }
378 if (!this->identity)
379 {
380 DBG1(DBG_CFG, "unable to initiate, missing --identity option");
381 terminate(pid);
382 return JOB_REQUEUE_NONE;
383 }
384
385 peer_cfg = create_peer_cfg(this);
386
387 if (!add_auth_cfgs(this, peer_cfg))
388 {
389 peer_cfg->destroy(peer_cfg);
390 terminate(pid);
391 return JOB_REQUEUE_NONE;
392 }
393
394 child_cfg = create_child_cfg(this);
395 peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
396
397 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
398 controller_cb_empty, NULL, 0) != SUCCESS)
399 {
400 terminate(pid);
401 }
402 return JOB_REQUEUE_NONE;
403 }
404
405 /**
406 * Create a traffic selector from string, add to list
407 */
408 static void add_ts(private_cmd_connection_t *this,
409 linked_list_t *list, char *string)
410 {
411 traffic_selector_t *ts;
412
413 ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
414 if (!ts)
415 {
416 DBG1(DBG_CFG, "invalid traffic selector: %s", string);
417 exit(1);
418 }
419 list->insert_last(list, ts);
420 }
421
422 /**
423 * Parse profile name identifier
424 */
425 static void set_profile(private_cmd_connection_t *this, char *name)
426 {
427 int profile;
428
429 profile = enum_from_name(profile_names, name);
430 if (profile == -1)
431 {
432 DBG1(DBG_CFG, "unknown connection profile: %s", name);
433 exit(1);
434 }
435 this->profile = profile;
436 }
437
438 METHOD(cmd_connection_t, handle, bool,
439 private_cmd_connection_t *this, cmd_option_type_t opt, char *arg)
440 {
441 proposal_t *proposal;
442
443 switch (opt)
444 {
445 case CMD_OPT_HOST:
446 this->host = arg;
447 break;
448 case CMD_OPT_REMOTE_IDENTITY:
449 this->server = arg;
450 break;
451 case CMD_OPT_IDENTITY:
452 this->identity = arg;
453 break;
454 case CMD_OPT_EAP_IDENTITY:
455 case CMD_OPT_XAUTH_USER:
456 this->xautheap = arg;
457 break;
458 case CMD_OPT_RSA:
459 case CMD_OPT_AGENT:
460 case CMD_OPT_PKCS12:
461 this->key_seen = TRUE;
462 break;
463 case CMD_OPT_LOCAL_TS:
464 add_ts(this, this->local_ts, arg);
465 break;
466 case CMD_OPT_REMOTE_TS:
467 add_ts(this, this->remote_ts, arg);
468 break;
469 case CMD_OPT_IKE_PROPOSAL:
470 proposal = proposal_create_from_string(PROTO_IKE, arg);
471 if (!proposal)
472 {
473 exit(1);
474 }
475 this->ike_proposals->insert_last(this->ike_proposals, proposal);
476 break;
477 case CMD_OPT_PROFILE:
478 set_profile(this, arg);
479 break;
480 default:
481 return FALSE;
482 }
483 return TRUE;
484 }
485
486 METHOD(cmd_connection_t, destroy, void,
487 private_cmd_connection_t *this)
488 {
489 this->ike_proposals->destroy_offset(this->ike_proposals,
490 offsetof(proposal_t, destroy));
491 this->local_ts->destroy_offset(this->local_ts,
492 offsetof(traffic_selector_t, destroy));
493 this->remote_ts->destroy_offset(this->remote_ts,
494 offsetof(traffic_selector_t, destroy));
495 free(this);
496 }
497
498 /**
499 * See header
500 */
501 cmd_connection_t *cmd_connection_create()
502 {
503 private_cmd_connection_t *this;
504
505 INIT(this,
506 .public = {
507 .handle = _handle,
508 .destroy = _destroy,
509 },
510 .pid = getpid(),
511 .local_ts = linked_list_create(),
512 .remote_ts = linked_list_create(),
513 .ike_proposals = linked_list_create(),
514 .profile = PROF_UNDEF,
515 );
516
517 /* always include the virtual IP in traffic selector list */
518 this->local_ts->insert_last(this->local_ts,
519 traffic_selector_create_dynamic(0, 0, 65535));
520
521 /* queue job, gets initiated as soon as we are up and running */
522 lib->processor->queue_job(lib->processor,
523 (job_t*)callback_job_create_with_prio(
524 (callback_job_cb_t)initiate, this, NULL,
525 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
526
527 return &this->public;
528 }