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