2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
5 * Copyright (C) 2013 Martin Willi
6 * Copyright (C) 2013 revosec AG
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>.
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
19 #include "cmd_connection.h"
24 #include <utils/debug.h>
25 #include <processing/jobs/callback_job.h>
26 #include <threading/thread.h>
29 typedef enum profile_t profile_t
;
30 typedef struct private_cmd_connection_t private_cmd_connection_t
;
33 * Connection profiles we support
50 ENUM(profile_names
, PROF_V2_PUB
, PROF_V1_HYBRID_AM
,
65 * Private data of an cmd_connection_t object.
67 struct private_cmd_connection_t
{
70 * Public cmd_connection_t interface.
72 cmd_connection_t
public;
75 * Process ID to terminate on failure
80 * List of local traffic selectors
82 linked_list_t
*local_ts
;
85 * List of remote traffic selectors
87 linked_list_t
*remote_ts
;
90 * List of IKE proposals
92 linked_list_t
*ike_proposals
;
95 * List of CHILD proposals
97 linked_list_t
*child_proposals
;
100 * Hostname to connect to
105 * Server identity, or NULL to use host
120 * Is a private key configured
125 * Selected connection profile
131 * Shut down application
133 static void terminate(pid_t pid
)
139 * Create peer config with associated ike config
141 static peer_cfg_t
* create_peer_cfg(private_cmd_connection_t
*this)
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
;
150 switch (this->profile
)
155 case PROF_V2_PUB_EAP
:
159 case PROF_V1_XAUTH_AM
:
160 case PROF_V1_XAUTH_PSK_AM
:
161 case PROF_V1_HYBRID_AM
:
166 case PROF_V1_XAUTH_PSK
:
172 local_port
= charon
->socket
->get_port(charon
->socket
, FALSE
);
173 if (local_port
!= IKEV2_UDP_PORT
)
175 remote_port
= IKEV2_NATT_PORT
;
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
))
181 while (this->ike_proposals
->remove_first(this->ike_proposals
,
182 (void**)&proposal
) == SUCCESS
)
184 ike_cfg
->add_proposal(ike_cfg
, proposal
);
189 ike_cfg
->add_proposal(ike_cfg
, proposal_create_default(PROTO_IKE
));
191 peer_cfg
= peer_cfg_create("cmd", ike_cfg
,
192 CERT_SEND_IF_ASKED
, UNIQUE_REPLACE
, 1, /* keyingtries */
193 36000, 0, /* rekey 10h, reauth none */
194 600, 600, /* jitter, over 10min */
195 TRUE
, aggressive
, TRUE
, /* mobike, aggressive, pull */
196 30, 0, /* DPD delay, timeout */
197 FALSE
, NULL
, NULL
); /* mediation */
198 peer_cfg
->add_virtual_ip(peer_cfg
, host_create_from_string("0.0.0.0", 0));
204 * Add a single auth cfg of given class to peer cfg
206 static void add_auth_cfg(private_cmd_connection_t
*this, peer_cfg_t
*peer_cfg
,
207 bool local
, auth_class_t
class)
209 identification_t
*id
;
212 auth
= auth_cfg_create();
213 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, class);
216 id
= identification_create_from_string(this->identity
);
222 auth
->add(auth
, AUTH_RULE_EAP_IDENTITY
,
223 identification_create_from_string(this->xautheap
));
225 case AUTH_CLASS_XAUTH
:
226 auth
->add(auth
, AUTH_RULE_XAUTH_IDENTITY
,
227 identification_create_from_string(this->xautheap
));
238 id
= identification_create_from_string(this->server
);
242 id
= identification_create_from_string(this->host
);
244 auth
->add(auth
, AUTH_RULE_IDENTITY_LOOSE
, TRUE
);
246 auth
->add(auth
, AUTH_RULE_IDENTITY
, id
);
247 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, local
);
251 * Attach authentication configs to peer config
253 static bool add_auth_cfgs(private_cmd_connection_t
*this, peer_cfg_t
*peer_cfg
)
255 if (this->profile
== PROF_UNDEF
)
259 this->profile
= PROF_V2_PUB
;
263 this->profile
= PROF_V2_EAP
;
266 switch (this->profile
)
269 case PROF_V2_PUB_EAP
:
273 case PROF_V1_XAUTH_AM
:
276 DBG1(DBG_CFG
, "missing private key for profile %N",
277 profile_names
, this->profile
);
285 switch (this->profile
)
288 add_auth_cfg(this, peer_cfg
, TRUE
, AUTH_CLASS_PUBKEY
);
289 add_auth_cfg(this, peer_cfg
, FALSE
, AUTH_CLASS_ANY
);
292 add_auth_cfg(this, peer_cfg
, TRUE
, AUTH_CLASS_EAP
);
293 add_auth_cfg(this, peer_cfg
, FALSE
, AUTH_CLASS_ANY
);
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
);
302 add_auth_cfg(this, peer_cfg
, TRUE
, AUTH_CLASS_PUBKEY
);
303 add_auth_cfg(this, peer_cfg
, FALSE
, AUTH_CLASS_PUBKEY
);
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
);
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
);
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
);
329 * Attach child config to peer config
331 static child_cfg_t
* create_child_cfg(private_cmd_connection_t
*this)
333 child_cfg_t
*child_cfg
;
334 traffic_selector_t
*ts
;
335 proposal_t
*proposal
;
336 lifetime_cfg_t lifetime
= {
338 .life
= 10800 /* 3h */,
339 .rekey
= 10200 /* 2h50min */,
340 .jitter
= 300 /* 5min */
344 child_cfg
= child_cfg_create("cmd", &lifetime
,
345 NULL
, FALSE
, MODE_TUNNEL
, /* updown, hostaccess */
346 ACTION_NONE
, ACTION_NONE
, ACTION_NONE
, FALSE
,
347 0, 0, NULL
, NULL
, 0);
348 if (this->child_proposals
->get_count(this->child_proposals
))
350 while (this->child_proposals
->remove_first(this->child_proposals
,
351 (void**)&proposal
) == SUCCESS
)
353 child_cfg
->add_proposal(child_cfg
, proposal
);
358 child_cfg
->add_proposal(child_cfg
, proposal_create_default(PROTO_ESP
));
360 while (this->local_ts
->remove_first(this->local_ts
, (void**)&ts
) == SUCCESS
)
362 child_cfg
->add_traffic_selector(child_cfg
, TRUE
, ts
);
364 if (this->remote_ts
->get_count(this->remote_ts
) == 0)
366 /* add a 0.0.0.0/0 TS for remote side if none given */
367 ts
= traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE
,
368 "0.0.0.0", 0, "255.255.255.255", 65535);
369 this->remote_ts
->insert_last(this->remote_ts
, ts
);
371 while (this->remote_ts
->remove_first(this->remote_ts
,
372 (void**)&ts
) == SUCCESS
)
374 child_cfg
->add_traffic_selector(child_cfg
, FALSE
, ts
);
381 * Initiate the configured connection
383 static job_requeue_t
initiate(private_cmd_connection_t
*this)
385 peer_cfg_t
*peer_cfg
;
386 child_cfg_t
*child_cfg
;
387 pid_t pid
= this->pid
;
391 DBG1(DBG_CFG
, "unable to initiate, missing --host option");
393 return JOB_REQUEUE_NONE
;
397 DBG1(DBG_CFG
, "unable to initiate, missing --identity option");
399 return JOB_REQUEUE_NONE
;
402 peer_cfg
= create_peer_cfg(this);
404 if (!add_auth_cfgs(this, peer_cfg
))
406 peer_cfg
->destroy(peer_cfg
);
408 return JOB_REQUEUE_NONE
;
411 child_cfg
= create_child_cfg(this);
412 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
->get_ref(child_cfg
));
414 if (charon
->controller
->initiate(charon
->controller
, peer_cfg
, child_cfg
,
415 controller_cb_empty
, NULL
, 0) != SUCCESS
)
419 return JOB_REQUEUE_NONE
;
423 * Create a traffic selector from string, add to list
425 static void add_ts(private_cmd_connection_t
*this,
426 linked_list_t
*list
, char *string
)
428 traffic_selector_t
*ts
;
430 ts
= traffic_selector_create_from_cidr(string
, 0, 0, 65535);
433 DBG1(DBG_CFG
, "invalid traffic selector: %s", string
);
436 list
->insert_last(list
, ts
);
440 * Parse profile name identifier
442 static void set_profile(private_cmd_connection_t
*this, char *name
)
446 profile
= enum_from_name(profile_names
, name
);
449 DBG1(DBG_CFG
, "unknown connection profile: %s", name
);
452 this->profile
= profile
;
455 METHOD(cmd_connection_t
, handle
, bool,
456 private_cmd_connection_t
*this, cmd_option_type_t opt
, char *arg
)
458 proposal_t
*proposal
;
465 case CMD_OPT_REMOTE_IDENTITY
:
468 case CMD_OPT_IDENTITY
:
469 this->identity
= arg
;
471 case CMD_OPT_EAP_IDENTITY
:
472 case CMD_OPT_XAUTH_USER
:
473 this->xautheap
= arg
;
478 this->key_seen
= TRUE
;
480 case CMD_OPT_LOCAL_TS
:
481 add_ts(this, this->local_ts
, arg
);
483 case CMD_OPT_REMOTE_TS
:
484 add_ts(this, this->remote_ts
, arg
);
486 case CMD_OPT_IKE_PROPOSAL
:
487 proposal
= proposal_create_from_string(PROTO_IKE
, arg
);
492 this->ike_proposals
->insert_last(this->ike_proposals
, proposal
);
494 case CMD_OPT_ESP_PROPOSAL
:
495 proposal
= proposal_create_from_string(PROTO_ESP
, arg
);
500 this->child_proposals
->insert_last(this->child_proposals
, proposal
);
502 case CMD_OPT_AH_PROPOSAL
:
503 proposal
= proposal_create_from_string(PROTO_AH
, arg
);
508 this->child_proposals
->insert_last(this->child_proposals
, proposal
);
510 case CMD_OPT_PROFILE
:
511 set_profile(this, arg
);
519 METHOD(cmd_connection_t
, destroy
, void,
520 private_cmd_connection_t
*this)
522 this->ike_proposals
->destroy_offset(this->ike_proposals
,
523 offsetof(proposal_t
, destroy
));
524 this->child_proposals
->destroy_offset(this->child_proposals
,
525 offsetof(proposal_t
, destroy
));
526 this->local_ts
->destroy_offset(this->local_ts
,
527 offsetof(traffic_selector_t
, destroy
));
528 this->remote_ts
->destroy_offset(this->remote_ts
,
529 offsetof(traffic_selector_t
, destroy
));
536 cmd_connection_t
*cmd_connection_create()
538 private_cmd_connection_t
*this;
546 .local_ts
= linked_list_create(),
547 .remote_ts
= linked_list_create(),
548 .ike_proposals
= linked_list_create(),
549 .child_proposals
= linked_list_create(),
550 .profile
= PROF_UNDEF
,
553 /* always include the virtual IP in traffic selector list */
554 this->local_ts
->insert_last(this->local_ts
,
555 traffic_selector_create_dynamic(0, 0, 65535));
557 /* queue job, gets initiated as soon as we are up and running */
558 lib
->processor
->queue_job(lib
->processor
,
559 (job_t
*)callback_job_create_with_prio(
560 (callback_job_cb_t
)initiate
, this, NULL
,
561 (callback_job_cancel_t
)return_false
, JOB_PRIO_CRITICAL
));
563 return &this->public;