/*
+ * Copyright (C) 2013 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
#include <utils/debug.h>
#include <processing/jobs/callback_job.h>
+#include <threading/thread.h>
#include <daemon.h>
+typedef enum profile_t profile_t;
typedef struct private_cmd_connection_t private_cmd_connection_t;
/**
+ * Connection profiles we support
+ */
+enum profile_t {
+ PROF_UNDEF,
+ PROF_V2_PUB,
+ PROF_V2_EAP,
+ PROF_V2_PUB_EAP,
+ PROF_V1_PUB,
+ PROF_V1_PUB_AM,
+ PROF_V1_XAUTH,
+ PROF_V1_XAUTH_AM,
+ PROF_V1_XAUTH_PSK,
+ PROF_V1_XAUTH_PSK_AM,
+ PROF_V1_HYBRID,
+ PROF_V1_HYBRID_AM,
+};
+
+ENUM(profile_names, PROF_V2_PUB, PROF_V1_HYBRID_AM,
+ "ikev2-pub",
+ "ikev2-eap",
+ "ikev2-pub-eap",
+ "ikev1-pub",
+ "ikev1-pub-am",
+ "ikev1-xauth",
+ "ikev1-xauth-am",
+ "ikev1-xauth-psk",
+ "ikev1-xauth-psk-am",
+ "ikev1-hybrid",
+ "ikev1-hybrid-am",
+);
+
+/**
* Private data of an cmd_connection_t object.
*/
struct private_cmd_connection_t {
pid_t pid;
/**
+ * List of local traffic selectors
+ */
+ linked_list_t *local_ts;
+
+ /**
+ * List of remote traffic selectors
+ */
+ linked_list_t *remote_ts;
+
+ /**
+ * List of IKE proposals
+ */
+ linked_list_t *ike_proposals;
+
+ /**
+ * List of CHILD proposals
+ */
+ linked_list_t *child_proposals;
+
+ /**
* Hostname to connect to
*/
char *host;
/**
+ * Server identity, or NULL to use host
+ */
+ char *server;
+
+ /**
* Local identity
*/
char *identity;
+
+ /**
+ * XAuth/EAP identity
+ */
+ char *xautheap;
+
+ /**
+ * Is a private key configured
+ */
+ bool key_seen;
+
+ /**
+ * Selected connection profile
+ */
+ profile_t profile;
};
/**
* Shut down application
*/
-static void terminate(private_cmd_connection_t *this)
+static void terminate(pid_t pid)
{
- kill(this->pid, SIGUSR1);
+ kill(pid, SIGUSR1);
}
/**
{
ike_cfg_t *ike_cfg;
peer_cfg_t *peer_cfg;
- u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
+ proposal_t *proposal;
+ ike_cfg_create_t ike = {
+ .local = "0.0.0.0",
+ .remote = this->host,
+ .remote_port = IKEV2_UDP_PORT,
+ .fragmentation = FRAGMENTATION_YES,
+ };
+ peer_cfg_create_t peer = {
+ .cert_policy = CERT_SEND_IF_ASKED,
+ .unique = UNIQUE_REPLACE,
+ .keyingtries = 1,
+ .rekey_time = 36000, /* 10h */
+ .jitter_time = 600, /* 10min */
+ .over_time = 600, /* 10min */
+ .dpd = 30,
+ };
- local_port = charon->socket->get_port(charon->socket, FALSE);
- if (local_port != IKEV2_UDP_PORT)
+ switch (this->profile)
{
- remote_port = IKEV2_NATT_PORT;
+ case PROF_UNDEF:
+ case PROF_V2_PUB:
+ case PROF_V2_EAP:
+ case PROF_V2_PUB_EAP:
+ ike.version = IKEV2;
+ break;
+ case PROF_V1_PUB_AM:
+ case PROF_V1_XAUTH_AM:
+ case PROF_V1_XAUTH_PSK_AM:
+ case PROF_V1_HYBRID_AM:
+ peer.aggressive = TRUE;
+ /* FALL */
+ case PROF_V1_PUB:
+ case PROF_V1_XAUTH:
+ case PROF_V1_XAUTH_PSK:
+ case PROF_V1_HYBRID:
+ ike.version = IKEV1;
+ break;
+ }
+
+ ike.local_port = charon->socket->get_port(charon->socket, FALSE);
+ if (ike.local_port != IKEV2_UDP_PORT)
+ {
+ ike.remote_port = IKEV2_NATT_PORT;
+ }
+ ike_cfg = ike_cfg_create(&ike);
+ if (this->ike_proposals->get_count(this->ike_proposals))
+ {
+ while (this->ike_proposals->remove_first(this->ike_proposals,
+ (void**)&proposal) == SUCCESS)
+ {
+ ike_cfg->add_proposal(ike_cfg, proposal);
+ }
}
- ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, local_port,
- this->host, FALSE, remote_port, FRAGMENTATION_NO, 0);
- ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
- peer_cfg = peer_cfg_create("cmd", ike_cfg,
- CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
- 36000, 0, /* rekey 10h, reauth none */
- 600, 600, /* jitter, over 10min */
- TRUE, FALSE, /* mobike, aggressive */
- 30, 0, /* DPD delay, timeout */
- FALSE, NULL, NULL); /* mediation */
- peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
+ else
+ {
+ ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
+ ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
+ }
+ peer_cfg = peer_cfg_create("cmd", ike_cfg, &peer);
return peer_cfg;
}
/**
- * Attach authentication configs to peer config
+ * Add a single auth cfg of given class to peer cfg
*/
-static void add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
+static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
+ bool local, auth_class_t class)
{
+ identification_t *id;
auth_cfg_t *auth;
auth = auth_cfg_create();
- auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
- auth->add(auth, AUTH_RULE_IDENTITY,
- identification_create_from_string(this->identity));
- peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
+ auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
+ if (local)
+ {
+ id = identification_create_from_string(this->identity);
+ if (this->xautheap)
+ {
+ switch (class)
+ {
+ case AUTH_CLASS_EAP:
+ auth->add(auth, AUTH_RULE_EAP_IDENTITY,
+ identification_create_from_string(this->xautheap));
+ break;
+ case AUTH_CLASS_XAUTH:
+ auth->add(auth, AUTH_RULE_XAUTH_IDENTITY,
+ identification_create_from_string(this->xautheap));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (this->server)
+ {
+ id = identification_create_from_string(this->server);
+ }
+ else
+ {
+ id = identification_create_from_string(this->host);
+ }
+ auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
+ }
+ auth->add(auth, AUTH_RULE_IDENTITY, id);
+ peer_cfg->add_auth_cfg(peer_cfg, auth, local);
+}
- auth = auth_cfg_create();
+/**
+ * Attach authentication configs to peer config
+ */
+static bool add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
+{
+ if (this->profile == PROF_UNDEF)
+ {
+ if (this->key_seen)
+ {
+ this->profile = PROF_V2_PUB;
+ }
+ else
+ {
+ this->profile = PROF_V2_EAP;
+ }
+ }
+ switch (this->profile)
+ {
+ case PROF_V2_PUB:
+ case PROF_V2_PUB_EAP:
+ case PROF_V1_PUB:
+ case PROF_V1_XAUTH:
+ case PROF_V1_PUB_AM:
+ case PROF_V1_XAUTH_AM:
+ if (!this->key_seen)
+ {
+ DBG1(DBG_CFG, "missing private key for profile %N",
+ profile_names, this->profile);
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
- auth->add(auth, AUTH_RULE_IDENTITY,
- identification_create_from_string(this->host));
- peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
+ switch (this->profile)
+ {
+ case PROF_V2_PUB:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
+ break;
+ case PROF_V2_EAP:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
+ break;
+ case PROF_V2_PUB_EAP:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
+ break;
+ case PROF_V1_PUB:
+ case PROF_V1_PUB_AM:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
+ break;
+ case PROF_V1_XAUTH:
+ case PROF_V1_XAUTH_AM:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
+ break;
+ case PROF_V1_XAUTH_PSK:
+ case PROF_V1_XAUTH_PSK_AM:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PSK);
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PSK);
+ break;
+ case PROF_V1_HYBRID:
+ case PROF_V1_HYBRID_AM:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
}
/**
* Attach child config to peer config
*/
-static child_cfg_t* create_child_cfg(private_cmd_connection_t *this)
+static child_cfg_t* create_child_cfg(private_cmd_connection_t *this,
+ peer_cfg_t *peer_cfg)
{
child_cfg_t *child_cfg;
traffic_selector_t *ts;
- lifetime_cfg_t lifetime = {
- .time = {
- .life = 10800 /* 3h */,
- .rekey = 10200 /* 2h50min */,
- .jitter = 300 /* 5min */
- }
+ proposal_t *proposal;
+ bool has_v4 = FALSE, has_v6 = FALSE;
+ child_cfg_create_t child = {
+ .lifetime = {
+ .time = {
+ .life = 10800 /* 3h */,
+ .rekey = 10200 /* 2h50min */,
+ .jitter = 300 /* 5min */
+ }
+ },
+ .mode = MODE_TUNNEL,
};
- child_cfg = child_cfg_create("cmd", &lifetime,
- NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
- ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
- 0, 0, NULL, NULL, 0);
- child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
- ts = traffic_selector_create_dynamic(0, 0, 65535);
- child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
- ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
+ child_cfg = child_cfg_create("cmd", &child);
+ if (this->child_proposals->get_count(this->child_proposals))
+ {
+ while (this->child_proposals->remove_first(this->child_proposals,
+ (void**)&proposal) == SUCCESS)
+ {
+ child_cfg->add_proposal(child_cfg, proposal);
+ }
+ }
+ else
+ {
+ child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
+ child_cfg->add_proposal(child_cfg,
+ proposal_create_default_aead(PROTO_ESP));
+ }
+ while (this->local_ts->remove_first(this->local_ts, (void**)&ts) == SUCCESS)
+ {
+ child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
+ }
+ if (this->remote_ts->get_count(this->remote_ts) == 0)
+ {
+ /* add a 0.0.0.0/0 TS for remote side if none given */
+ ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
"0.0.0.0", 0, "255.255.255.255", 65535);
- child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
+ this->remote_ts->insert_last(this->remote_ts, ts);
+ has_v4 = TRUE;
+ }
+ while (this->remote_ts->remove_first(this->remote_ts,
+ (void**)&ts) == SUCCESS)
+ {
+ switch (ts->get_type(ts))
+ {
+ case TS_IPV4_ADDR_RANGE:
+ has_v4 = TRUE;
+ break;
+ case TS_IPV6_ADDR_RANGE:
+ has_v6 = TRUE;
+ break;
+ }
+ child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
+ }
+ if (has_v4)
+ {
+ peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
+ }
+ if (has_v6)
+ {
+ peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("::", 0));
+ }
+ peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
return child_cfg;
}
{
peer_cfg_t *peer_cfg;
child_cfg_t *child_cfg;
+ pid_t pid = this->pid;
if (!this->host)
{
DBG1(DBG_CFG, "unable to initiate, missing --host option");
- terminate(this);
+ terminate(pid);
return JOB_REQUEUE_NONE;
}
if (!this->identity)
{
DBG1(DBG_CFG, "unable to initiate, missing --identity option");
- terminate(this);
+ terminate(pid);
return JOB_REQUEUE_NONE;
}
peer_cfg = create_peer_cfg(this);
- add_auth_cfgs(this, peer_cfg);
+ if (!add_auth_cfgs(this, peer_cfg))
+ {
+ peer_cfg->destroy(peer_cfg);
+ terminate(pid);
+ return JOB_REQUEUE_NONE;
+ }
- child_cfg = create_child_cfg(this);
- peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
+ child_cfg = create_child_cfg(this, peer_cfg);
if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
- controller_cb_empty, NULL, 0) != SUCCESS)
+ controller_cb_empty, NULL, 0, FALSE) != SUCCESS)
{
- terminate(this);
+ terminate(pid);
}
return JOB_REQUEUE_NONE;
}
+/**
+ * Create a traffic selector from string, add to list
+ */
+static void add_ts(private_cmd_connection_t *this,
+ linked_list_t *list, char *string)
+{
+ traffic_selector_t *ts;
+
+ ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
+ if (!ts)
+ {
+ DBG1(DBG_CFG, "invalid traffic selector: %s", string);
+ exit(1);
+ }
+ list->insert_last(list, ts);
+}
+
+/**
+ * Parse profile name identifier
+ */
+static void set_profile(private_cmd_connection_t *this, char *name)
+{
+ profile_t profile;
+
+ if (!enum_from_name(profile_names, name, &profile))
+ {
+ DBG1(DBG_CFG, "unknown connection profile: %s", name);
+ exit(1);
+ }
+ this->profile = profile;
+}
+
METHOD(cmd_connection_t, handle, bool,
private_cmd_connection_t *this, cmd_option_type_t opt, char *arg)
{
+ proposal_t *proposal;
+
switch (opt)
{
case CMD_OPT_HOST:
this->host = arg;
break;
+ case CMD_OPT_REMOTE_IDENTITY:
+ this->server = arg;
+ break;
case CMD_OPT_IDENTITY:
this->identity = arg;
break;
+ case CMD_OPT_EAP_IDENTITY:
+ case CMD_OPT_XAUTH_USER:
+ this->xautheap = arg;
+ break;
+ case CMD_OPT_RSA:
+ case CMD_OPT_AGENT:
+ case CMD_OPT_PKCS12:
+ this->key_seen = TRUE;
+ break;
+ case CMD_OPT_LOCAL_TS:
+ add_ts(this, this->local_ts, arg);
+ break;
+ case CMD_OPT_REMOTE_TS:
+ add_ts(this, this->remote_ts, arg);
+ break;
+ case CMD_OPT_IKE_PROPOSAL:
+ proposal = proposal_create_from_string(PROTO_IKE, arg);
+ if (!proposal)
+ {
+ exit(1);
+ }
+ this->ike_proposals->insert_last(this->ike_proposals, proposal);
+ break;
+ case CMD_OPT_ESP_PROPOSAL:
+ proposal = proposal_create_from_string(PROTO_ESP, arg);
+ if (!proposal)
+ {
+ exit(1);
+ }
+ this->child_proposals->insert_last(this->child_proposals, proposal);
+ break;
+ case CMD_OPT_AH_PROPOSAL:
+ proposal = proposal_create_from_string(PROTO_AH, arg);
+ if (!proposal)
+ {
+ exit(1);
+ }
+ this->child_proposals->insert_last(this->child_proposals, proposal);
+ break;
+ case CMD_OPT_PROFILE:
+ set_profile(this, arg);
+ break;
default:
return FALSE;
}
METHOD(cmd_connection_t, destroy, void,
private_cmd_connection_t *this)
{
+ this->ike_proposals->destroy_offset(this->ike_proposals,
+ offsetof(proposal_t, destroy));
+ this->child_proposals->destroy_offset(this->child_proposals,
+ offsetof(proposal_t, destroy));
+ this->local_ts->destroy_offset(this->local_ts,
+ offsetof(traffic_selector_t, destroy));
+ this->remote_ts->destroy_offset(this->remote_ts,
+ offsetof(traffic_selector_t, destroy));
free(this);
}
.destroy = _destroy,
},
.pid = getpid(),
+ .local_ts = linked_list_create(),
+ .remote_ts = linked_list_create(),
+ .ike_proposals = linked_list_create(),
+ .child_proposals = linked_list_create(),
+ .profile = PROF_UNDEF,
);
+ /* always include the virtual IP in traffic selector list */
+ this->local_ts->insert_last(this->local_ts,
+ traffic_selector_create_dynamic(0, 0, 65535));
+
/* queue job, gets initiated as soon as we are up and running */
lib->processor->queue_job(lib->processor,
(job_t*)callback_job_create_with_prio(