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