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