diff options
| author | Tobias Brunner <tobias@strongswan.org> | 2017-02-16 19:24:17 +0100 |
|---|---|---|
| committer | Tobias Brunner <tobias@strongswan.org> | 2017-02-16 19:24:17 +0100 |
| commit | 2bb314c17b3293f4c4a504cee7d5daa19de4a2b5 (patch) | |
| tree | da98466d54e8ae783b617ded0313d7109de818c8 /src/libcharon/plugins | |
| parent | c2129d1cbe6b482cf956a1f609ff974c9dcd5b24 (diff) | |
| parent | e16d1005f7c14b261a4d91f63159f96688fd8f00 (diff) | |
| download | strongswan-2bb314c17b3293f4c4a504cee7d5daa19de4a2b5.tar.bz2 strongswan-2bb314c17b3293f4c4a504cee7d5daa19de4a2b5.tar.xz | |
Merge branch 'vici-updates'
Adds several new features for the VICI interface and swanctl.
Diffstat (limited to 'src/libcharon/plugins')
| -rw-r--r-- | src/libcharon/plugins/bypass_lan/bypass_lan_listener.c | 4 | ||||
| -rw-r--r-- | src/libcharon/plugins/medcli/medcli_config.c | 81 | ||||
| -rw-r--r-- | src/libcharon/plugins/sql/sql_config.c | 10 | ||||
| -rw-r--r-- | src/libcharon/plugins/stroke/stroke_config.c | 23 | ||||
| -rw-r--r-- | src/libcharon/plugins/stroke/stroke_control.c | 20 | ||||
| -rw-r--r-- | src/libcharon/plugins/stroke/stroke_list.c | 2 | ||||
| -rw-r--r-- | src/libcharon/plugins/unity/unity_handler.c | 5 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/README.md | 97 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/python/vici/session.py | 6 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/ruby/lib/vici.rb | 4 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_attribute.c | 12 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_authority.c | 107 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_config.c | 326 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_control.c | 136 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_cred.c | 210 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_dispatcher.c | 6 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_logger.c | 7 | ||||
| -rw-r--r-- | src/libcharon/plugins/vici/vici_query.c | 81 |
18 files changed, 993 insertions, 144 deletions
diff --git a/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c b/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c index 138f8fb0b..e690028f2 100644 --- a/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c +++ b/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c @@ -79,7 +79,7 @@ static void bypass_policy_destroy(bypass_policy_t *this) ts = traffic_selector_create_from_subnet(this->net->clone(this->net), this->mask, 0, 0, 65535); DBG1(DBG_IKE, "uninstalling bypass policy for %R", ts); - charon->shunts->uninstall(charon->shunts, + charon->shunts->uninstall(charon->shunts, "bypass-lan", this->cfg->get_name(this->cfg)); this->cfg->destroy(this->cfg); ts->destroy(ts); @@ -173,7 +173,7 @@ static job_requeue_t update_bypass(private_bypass_lan_listener_t *this) cfg = child_cfg_create(name, &child); cfg->add_traffic_selector(cfg, FALSE, ts->clone(ts)); cfg->add_traffic_selector(cfg, TRUE, ts); - charon->shunts->install(charon->shunts, cfg); + charon->shunts->install(charon->shunts, "bypass-lan", cfg); DBG1(DBG_IKE, "installed bypass policy for %R", ts); INIT(found, diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 4452739c1..78159c845 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -24,6 +24,11 @@ typedef struct private_medcli_config_t private_medcli_config_t; /** + * Name of the mediation connection + */ +#define MEDIATION_CONN_NAME "medcli-mediation" + +/** * Private data of an medcli_config_t object */ struct private_medcli_config_t { @@ -72,36 +77,19 @@ static traffic_selector_t *ts_from_string(char *str) return traffic_selector_create_dynamic(0, 0, 65535); } -METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, - private_medcli_config_t *this, char *name) +/** + * Build a mediation config + */ +static peer_cfg_t *build_mediation_config(private_medcli_config_t *this, + peer_cfg_create_t *defaults) { enumerator_t *e; - peer_cfg_t *peer_cfg, *med_cfg; auth_cfg_t *auth; ike_cfg_t *ike_cfg; - child_cfg_t *child_cfg; + peer_cfg_t *med_cfg; + peer_cfg_create_t peer = *defaults; chunk_t me, other; - char *address, *local_net, *remote_net; - peer_cfg_create_t peer = { - .cert_policy = CERT_NEVER_SEND, - .unique = UNIQUE_REPLACE, - .keyingtries = 1, - .rekey_time = this->rekey * 60, - .jitter_time = this->rekey * 5, - .over_time = this->rekey * 3, - .dpd = this->dpd, - .mediation = TRUE, - }; - child_cfg_create_t child = { - .lifetime = { - .time = { - .life = this->rekey * 60 + this->rekey, - .rekey = this->rekey, - .jitter = this->rekey - }, - }, - .mode = MODE_TUNNEL, - }; + char *address; /* query mediation server config: * - build ike_cfg/peer_cfg for mediation connection on-the-fly @@ -120,7 +108,9 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, address, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE)); - med_cfg = peer_cfg_create("mediation", ike_cfg, &peer); + + peer.mediation = TRUE; + med_cfg = peer_cfg_create(MEDIATION_CONN_NAME, ike_cfg, &peer); e->destroy(e); auth = auth_cfg_create(); @@ -133,6 +123,42 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_encoding(ID_KEY_ID, other)); med_cfg->add_auth_cfg(med_cfg, auth, FALSE); + return med_cfg; +} + +METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, + private_medcli_config_t *this, char *name) +{ + enumerator_t *e; + auth_cfg_t *auth; + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; + chunk_t me, other; + char *local_net, *remote_net; + peer_cfg_create_t peer = { + .cert_policy = CERT_NEVER_SEND, + .unique = UNIQUE_REPLACE, + .keyingtries = 1, + .rekey_time = this->rekey * 60, + .jitter_time = this->rekey * 5, + .over_time = this->rekey * 3, + .dpd = this->dpd, + }; + child_cfg_create_t child = { + .lifetime = { + .time = { + .life = this->rekey * 60 + this->rekey, + .rekey = this->rekey, + .jitter = this->rekey + }, + }, + .mode = MODE_TUNNEL, + }; + + if (streq(name, "medcli-mediation")) + { + return build_mediation_config(this, &peer); + } /* query mediated config: * - use any-any ike_cfg @@ -150,8 +176,7 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, DESTROY_IF(e); return NULL; } - peer.mediation = FALSE; - peer.mediated_by = med_cfg; + peer.mediated_by = MEDIATION_CONN_NAME; peer.peer_id = identification_create_from_encoding(ID_KEY_ID, other); peer_cfg = peer_cfg_create(name, this->ike->get_ref(this->ike), &peer); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index bbc20dca7..88cac7f26 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -381,12 +381,14 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, ike = get_ike_cfg_by_id(this, ike_cfg); #ifdef ME - mediated_cfg = mediated_by ? get_peer_cfg_by_id(this, mediated_by) : NULL; + mediated_cfg = mediated_by ? get_peer_cfg_by_id(this, mediated_by) + : NULL; if (p_type) { peer_id = identification_create_from_encoding(p_type, p_data); } -#endif +#endif /* ME */ + if (virtual) { vip = host_create_from_string(virtual, 0); @@ -405,7 +407,8 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, .dpd = dpd_delay, #ifdef ME .mediation = mediation, - .mediated_by = mediated_cfg, + .mediated_by = mediated_cfg ? + mediated_cfg->get_name(mediated_cfg) : NULL, .peer_id = peer_id, #endif /* ME */ }; @@ -443,6 +446,7 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, } peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE); add_child_cfgs(this, peer_cfg, id); + DESTROY_IF(mediated_cfg); return peer_cfg; } DESTROY_IF(ike); diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 49bf3ab60..bbdc2116d 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -642,28 +642,9 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, /* force unique connections for mediation connections */ msg->add_conn.unique = 1; } - - if (msg->add_conn.ikeme.mediated_by) + else if (msg->add_conn.ikeme.mediated_by) { - peer_cfg_t *mediated_by; - - mediated_by = charon->backends->get_peer_cfg_by_name( - charon->backends, msg->add_conn.ikeme.mediated_by); - if (!mediated_by) - { - DBG1(DBG_CFG, "mediation connection '%s' not found, aborting", - msg->add_conn.ikeme.mediated_by); - return NULL; - } - if (!mediated_by->is_mediation(mediated_by)) - { - DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is " - "no mediation connection, aborting", - msg->add_conn.ikeme.mediated_by, msg->add_conn.name); - mediated_by->destroy(mediated_by); - return NULL; - } - peer.mediated_by = mediated_by; + peer.mediated_by = msg->add_conn.ikeme.mediated_by; if (msg->add_conn.ikeme.peerid) { peer.peer_id = identification_create_from_string( diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index fb60d3973..ee8306772 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -641,7 +641,8 @@ static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg, mode = child_cfg->get_mode(child_cfg); if (mode == MODE_PASS || mode == MODE_DROP) { - if (charon->shunts->install(charon->shunts, child_cfg)) + if (charon->shunts->install(charon->shunts, + peer_cfg->get_name(peer_cfg), child_cfg)) { fprintf(out, "'%s' shunt %N policy installed\n", name, ipsec_mode_names, mode); @@ -729,15 +730,30 @@ METHOD(stroke_control_t, route, void, METHOD(stroke_control_t, unroute, void, private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) { + child_cfg_t *child_cfg; child_sa_t *child_sa; enumerator_t *enumerator; + char *ns, *found = NULL; uint32_t id = 0; - if (charon->shunts->uninstall(charon->shunts, msg->unroute.name)) + enumerator = charon->shunts->create_enumerator(charon->shunts); + while (enumerator->enumerate(enumerator, &ns, &child_cfg)) { + if (ns && streq(msg->unroute.name, child_cfg->get_name(child_cfg))) + { + found = strdup(ns); + break; + } + } + enumerator->destroy(enumerator); + if (found && charon->shunts->uninstall(charon->shunts, found, + msg->unroute.name)) + { + free(found); fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name); return; } + free(found); enumerator = charon->traps->create_enumerator(charon->traps); while (enumerator->enumerate(enumerator, NULL, &child_sa)) diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c index cec26579d..92e368669 100644 --- a/src/libcharon/plugins/stroke/stroke_list.c +++ b/src/libcharon/plugins/stroke/stroke_list.c @@ -603,7 +603,7 @@ METHOD(stroke_list_t, status, void, /* Enumerate shunt policies */ first = TRUE; enumerator = charon->shunts->create_enumerator(charon->shunts); - while (enumerator->enumerate(enumerator, &child_cfg)) + while (enumerator->enumerate(enumerator, NULL, &child_cfg)) { if (name && !streq(name, child_cfg->get_name(child_cfg))) { diff --git a/src/libcharon/plugins/unity/unity_handler.c b/src/libcharon/plugins/unity/unity_handler.c index 570727823..25e0756b7 100644 --- a/src/libcharon/plugins/unity/unity_handler.c +++ b/src/libcharon/plugins/unity/unity_handler.c @@ -235,7 +235,7 @@ static job_requeue_t add_exclude_async(entry_t *entry) enumerator->destroy(enumerator); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - charon->shunts->install(charon->shunts, child_cfg); + charon->shunts->install(charon->shunts, "unity", child_cfg); child_cfg->destroy(child_cfg); DBG1(DBG_IKE, "installed %N bypass policy for %R", @@ -310,7 +310,8 @@ static bool remove_exclude(private_unity_handler_t *this, chunk_t data) DBG1(DBG_IKE, "uninstalling %N bypass policy for %R", configuration_attribute_type_names, UNITY_LOCAL_LAN, ts); ts->destroy(ts); - success = charon->shunts->uninstall(charon->shunts, name) && success; + success = charon->shunts->uninstall(charon->shunts, "unity", + name) && success; } list->destroy(list); return success; diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 4e53d7cc9..ba1e5f84a 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -283,12 +283,29 @@ Terminates an SA while streaming _control-log_ events. loglevel = <loglevel to issue "control-log" events for> } => { success = <yes or no> + matches = <number of matched SAs> + terminated = <number of terminated SAs> errmsg = <error string on failure or timeout> } The default timeout of 0 waits indefinitely for a result, and a timeout value of -1 returns a result immediately. +### rekey() ### + +Initiate the rekeying of an SA. + + { + child = <rekey a CHILD_SA by configuration name> + ike = <rekey an IKE_SA by configuration name> + child-id = <rekey a CHILD_SA by its reqid> + ike-id = <rekey an IKE_SA by its unique id> + } => { + success = <yes or no> + matches = <number of matched SAs> + errmsg = <error string on failure> + } + ### redirect() ### Redirect a client-initiated IKE_SA to another gateway. Only for IKEv2 and if @@ -303,6 +320,7 @@ supported by the peer. wildcards> } => { success = <yes or no> + matches = <number of matched SAs> errmsg = <error string on failure> } @@ -312,7 +330,7 @@ Install a trap, drop or bypass policy defined by a CHILD_SA config. { child = <CHILD_SA configuration name to install> - ike = <optional IKE_SA configuraiton name to find child under> + ike = <optional IKE_SA configuration name to find child under> } => { success = <yes or no> errmsg = <error string on failure> @@ -324,6 +342,8 @@ Uninstall a trap, drop or bypass policy defined by a CHILD_SA config. { child = <CHILD_SA configuration name to install> + ike = <optional IKE_SA configuration name to find child under, + if not given the first policy matching child is removed> } => { success = <yes or no> errmsg = <error string on failure> @@ -352,6 +372,7 @@ _list-policy_ events. pass = <set to yes to list bypass policies> trap = <set to yes to list trap policies> child = <filter by CHILD_SA configuration name> + ike = <filter by IKE_SA configuration name> } => { # completes after streaming list-sa events } @@ -466,12 +487,53 @@ Load a private key into the daemon. errmsg = <error string on failure> } +### unload-key() ### + +Unload the private key with the given key identifier. + + { + id = <hex-encoded SHA-1 key identifier of the private key to unload> + } => { + success = <yes or no> + errmsg = <error string on failure> + } + +### get-keys() ### + +Return a list of identifiers of private keys loaded exclusively over vici, not +including keys found in other backends. + + {} => { + keys = [ + <list of hex-encoded SHA-1 key identifiers> + ] + } + +### load-token() ### + +Load a private key located on a token into the daemon. Such keys may be listed +and unloaded using the _get-keys_ and _unload-key_ commands, respectively (based +on the key identifier derived from the public key). + + { + handle = <hex-encoded CKA_ID of the private key on token> + slot = <optional slot number> + module = <optional PKCS#11 module> + pin = <optional PIN to access the key, has to be provided via other + means if not given> + } => { + success = <yes or no> + errmsg = <error string on failure> + id = <hex-encoded SHA-1 key identifier of the public key on success> + } + ### load-shared() ### Load a shared IKE PSK, EAP or XAuth secret into the daemon. { - type = <private key type, IKE|EAP|XAUTH> + id = <optional unique identifier of this shared key> + type = <shared key type, IKE|EAP|XAUTH> data = <raw shared key data> owners = [ <list of shared key owner identities> @@ -481,6 +543,29 @@ Load a shared IKE PSK, EAP or XAuth secret into the daemon. errmsg = <error string on failure> } +### unload-shared() ### + +Unload a previously loaded shared IKE PSK, EAP or XAuth secret by its unique +identifier. + + { + id = <unique identifier of the shared key to unload> + } => { + success = <yes or no> + errmsg = <error string on failure> + } + +### get-shared() ### + +Return a list of unique identifiers of shared keys loaded exclusively over vici, +not including keys found in other backends. + + {} => { + keys = [ + <list of unique identifiers> + ] + } + ### flush-certs() ### Flushes the certificate cache. The optional type argument allows to flush @@ -569,6 +654,7 @@ List the currently loaded pools. { leases = <set to yes to include leases> + name = <optional name of the pool to query> } => { <pool name>* = { base = <virtual IP pool base address> @@ -678,7 +764,8 @@ command. <list of tasks currently handling passively> ] child-sas = { - <child-sa-name>* = { + <unique child-sa-name>* = { + name = <name of the CHILD_SA> uniqueid = <unique CHILD_SA identifier> reqid = <reqid of CHILD_SA> state = <state string of CHILD_SA> @@ -726,7 +813,9 @@ The _list-policy_ event is issued to stream installed policies during an active _list-policies_ command. { - <child-sa-config-name> = { + <ike-sa-config-name/child-sa-config-name> = { + child = <CHILD_SA configuration name> + ike = <IKE_SA configuration name or namespace, if available> mode = <policy mode, tunnel|transport|pass|drop> local-ts = [ <list of local traffic selectors> diff --git a/src/libcharon/plugins/vici/python/vici/session.py b/src/libcharon/plugins/vici/python/vici/session.py index 5bd4b7c40..1383fa778 100644 --- a/src/libcharon/plugins/vici/python/vici/session.py +++ b/src/libcharon/plugins/vici/python/vici/session.py @@ -208,13 +208,15 @@ class Session(object): """ self.handler.request("unload-pool", pool_name) - def get_pools(self): + def get_pools(self, options): """Retrieve loaded pools. + :param options: filter by name and/or retrieve leases (optional) + :type options: dict :return: loaded pools :rtype: dict """ - return self.handler.request("get-pools") + return self.handler.request("get-pools", options) def listen(self, event_types): """Register and listen for the given events. diff --git a/src/libcharon/plugins/vici/ruby/lib/vici.rb b/src/libcharon/plugins/vici/ruby/lib/vici.rb index 1a95fc3dd..bcf1a17be 100644 --- a/src/libcharon/plugins/vici/ruby/lib/vici.rb +++ b/src/libcharon/plugins/vici/ruby/lib/vici.rb @@ -492,8 +492,8 @@ module Vici ## # Get the currently loaded pools. - def get_pools() - @transp.request("get-pools").root + def get_pools(options) + @transp.request("get-pools", Message.new(options)).root end ## diff --git a/src/libcharon/plugins/vici/vici_attribute.c b/src/libcharon/plugins/vici/vici_attribute.c index e0d9b4ae8..4e1fa9708 100644 --- a/src/libcharon/plugins/vici/vici_attribute.c +++ b/src/libcharon/plugins/vici/vici_attribute.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2014-2015 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2014-2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG @@ -668,10 +668,11 @@ CALLBACK(get_pools, vici_message_t*, identification_t *uid; host_t *lease; bool list_leases, on; - char buf[32]; + char buf[32], *filter; int i; list_leases = message->get_bool(message, FALSE, "leases"); + filter = message->get_str(message, NULL, "name"); builder = vici_builder_create(); @@ -679,6 +680,11 @@ CALLBACK(get_pools, vici_message_t*, enumerator = this->pools->create_enumerator(this->pools); while (enumerator->enumerate(enumerator, &name, &pool)) { + if (filter && !streq(name, filter)) + { + continue; + } + vips = pool->vips; builder->begin_section(builder, name); diff --git a/src/libcharon/plugins/vici/vici_authority.c b/src/libcharon/plugins/vici/vici_authority.c index 94a7f68f6..0fa158b32 100644 --- a/src/libcharon/plugins/vici/vici_authority.c +++ b/src/libcharon/plugins/vici/vici_authority.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016 Tobias Brunner * Copyright (C) 2015 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -199,9 +200,28 @@ typedef struct { typedef struct { request_data_t *request; authority_t *authority; + char *handle; + uint32_t slot; + char *module; + char *file; } load_data_t; /** + * Clean up data associated with an authority load + */ +static void free_load_data(load_data_t *data) +{ + if (data->authority) + { + authority_destroy(data->authority); + } + free(data->handle); + free(data->module); + free(data->file); + free(data); +} + +/** * Parse a string */ CALLBACK(parse_string, bool, @@ -217,6 +237,28 @@ CALLBACK(parse_string, bool, } /** + * Parse a uint32_t + */ +CALLBACK(parse_uint32, bool, + uint32_t *out, chunk_t v) +{ + char buf[16], *end; + u_long l; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + l = strtoul(buf, &end, 0); + if (*end == 0) + { + *out = l; + return TRUE; + } + return FALSE; +} + +/** * Parse list of URIs */ CALLBACK(parse_uris, bool, @@ -266,8 +308,12 @@ CALLBACK(authority_kv, bool, load_data_t *data, vici_message_t *message, char *name, chunk_t value) { parse_rule_t rules[] = { - { "cacert", parse_cacert, &data->authority->cert }, - { "cert_uri_base", parse_string, &data->authority->cert_uri_base }, + { "cacert", parse_cacert, &data->authority->cert }, + { "file", parse_string, &data->file }, + { "handle", parse_string, &data->handle }, + { "slot", parse_uint32, &data->slot }, + { "module", parse_string, &data->module }, + { "cert_uri_base", parse_string, &data->authority->cert_uri_base }, }; return parse_rules(rules, countof(rules), name, value, @@ -341,21 +387,60 @@ CALLBACK(authority_sn, bool, linked_list_t *authorities; authority_t *authority; vici_cred_t *cred; + load_data_t *data; + chunk_t handle; - load_data_t data = { + INIT(data, .request = request, .authority = authority_create(name), - }; + .slot = -1, + ); DBG2(DBG_CFG, " authority %s:", name); - if (!message->parse(message, ctx, NULL, authority_kv, authority_li, &data) || - !data.authority->cert) + if (!message->parse(message, ctx, NULL, authority_kv, authority_li, data)) + { + free_load_data(data); + return FALSE; + } + if (!data->authority->cert) + { + if (data->file) + { + data->authority->cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, data->file, BUILD_END); + } + else if (data->handle) + { + handle = chunk_from_hex(chunk_from_str(data->handle), NULL); + if (data->slot != -1) + { + data->authority->cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_PKCS11_KEYID, handle, + BUILD_PKCS11_SLOT, data->slot, + data->module ? BUILD_PKCS11_MODULE : BUILD_END, + data->module, BUILD_END); + } + else + { + data->authority->cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_PKCS11_KEYID, handle, + data->module ? BUILD_PKCS11_MODULE : BUILD_END, + data->module, BUILD_END); + } + chunk_free(&handle); + } + } + if (!data->authority->cert) { - authority_destroy(data.authority); + request->reply = create_reply("CA certificate missing: %s", name); + free_load_data(data); return FALSE; } - log_authority_data(data.authority); + log_authority_data(data->authority); request->this->lock->write_lock(request->this->lock); @@ -372,12 +457,14 @@ CALLBACK(authority_sn, bool, } } enumerator->destroy(enumerator); - authorities->insert_last(authorities, data.authority); + authorities->insert_last(authorities, data->authority); cred = request->this->cred; - data.authority->cert = cred->add_cert(cred, data.authority->cert); + data->authority->cert = cred->add_cert(cred, data->authority->cert); + data->authority = NULL; request->this->lock->unlock(request->this->lock); + free_load_data(data); return TRUE; } diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 2110fd31d..12497ec5e 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -2,7 +2,7 @@ * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * - * Copyright (C) 2015-2016 Tobias Brunner + * Copyright (C) 2015-2017 Tobias Brunner * Copyright (C) 2015-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -247,6 +247,28 @@ typedef struct { } request_data_t; /** + * Certificate data + */ +typedef struct { + request_data_t *request; + char *handle; + uint32_t slot; + char *module; + char *file; +} cert_data_t; + +/** + * Clean up certificate data + */ +static void free_cert_data(cert_data_t *data) +{ + free(data->handle); + free(data->module); + free(data->file); + free(data); +} + +/** * Auth config data */ typedef struct { @@ -295,6 +317,12 @@ typedef struct { uint64_t rekey_time; uint64_t over_time; uint64_t rand_time; + uint8_t dscp; +#ifdef ME + bool mediation; + char *mediated_by; + identification_t *peer_id; +#endif /* ME */ } peer_data_t; /** @@ -370,6 +398,7 @@ static void log_peer_data(peer_data_t *data) DBG2(DBG_CFG, " send_cert = %N", cert_policy_names, data->send_cert); DBG2(DBG_CFG, " mobike = %u", data->mobike); DBG2(DBG_CFG, " aggressive = %u", data->aggressive); + DBG2(DBG_CFG, " dscp = 0x%.2x", data->dscp); DBG2(DBG_CFG, " encap = %u", data->encap); DBG2(DBG_CFG, " dpd_delay = %llu", data->dpd_delay); DBG2(DBG_CFG, " dpd_timeout = %llu", data->dpd_timeout); @@ -381,6 +410,14 @@ static void log_peer_data(peer_data_t *data) DBG2(DBG_CFG, " over_time = %llu", data->over_time); DBG2(DBG_CFG, " rand_time = %llu", data->rand_time); DBG2(DBG_CFG, " proposals = %#P", data->proposals); +#ifdef ME + DBG2(DBG_CFG, " mediation = %u", data->mediation); + if (data->mediated_by) + { + DBG2(DBG_CFG, " mediated_by = %s", data->mediated_by); + DBG2(DBG_CFG, " mediation_peer = %Y", data->peer_id); + } +#endif /* ME */ if (data->vips->get_count(data->vips)) { @@ -425,6 +462,10 @@ static void free_peer_data(peer_data_t *data) free(data->pools); free(data->local_addrs); free(data->remote_addrs); +#ifdef ME + free(data->mediated_by); + DESTROY_IF(data->peer_id); +#endif /* ME */ } /** @@ -461,7 +502,8 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " updown = %s", cfg->updown); DBG2(DBG_CFG, " hostaccess = %u", cfg->hostaccess); DBG2(DBG_CFG, " ipcomp = %u", cfg->ipcomp); - DBG2(DBG_CFG, " mode = %N", ipsec_mode_names, cfg->mode); + DBG2(DBG_CFG, " mode = %N%s", ipsec_mode_names, cfg->mode, + cfg->proxy_mode ? "_PROXY" : ""); DBG2(DBG_CFG, " policies = %u", data->policies); DBG2(DBG_CFG, " policies_fwd_out = %u", data->policies_fwd_out); if (data->replay_window != REPLAY_UNDEFINED) @@ -770,20 +812,22 @@ CALLBACK(parse_bool, bool, * Parse a ipsec_mode_t */ CALLBACK(parse_mode, bool, - ipsec_mode_t *out, chunk_t v) + child_cfg_create_t *cfg, chunk_t v) { enum_map_t map[] = { - { "tunnel", MODE_TUNNEL }, - { "transport", MODE_TRANSPORT }, - { "beet", MODE_BEET }, - { "drop", MODE_DROP }, - { "pass", MODE_PASS }, + { "tunnel", MODE_TUNNEL }, + { "transport", MODE_TRANSPORT }, + { "transport_proxy", MODE_TRANSPORT }, + { "beet", MODE_BEET }, + { "drop", MODE_DROP }, + { "pass", MODE_PASS }, }; int d; if (parse_map(map, countof(map), &d, v)) { - *out = d; + cfg->mode = d; + cfg->proxy_mode = (d == MODE_TRANSPORT) && (v.len > 9); return TRUE; } return FALSE; @@ -814,10 +858,9 @@ CALLBACK(parse_action, bool, } /** - * Parse a uint32_t + * Parse a uint32_t with the given base */ -CALLBACK(parse_uint32, bool, - uint32_t *out, chunk_t v) +static bool parse_uint32_base(uint32_t *out, chunk_t v, int base) { char buf[16], *end; u_long l; @@ -826,7 +869,7 @@ CALLBACK(parse_uint32, bool, { return FALSE; } - l = strtoul(buf, &end, 0); + l = strtoul(buf, &end, base); if (*end == 0) { *out = l; @@ -836,6 +879,24 @@ CALLBACK(parse_uint32, bool, } /** + * Parse a uint32_t + */ +CALLBACK(parse_uint32, bool, + uint32_t *out, chunk_t v) +{ + return parse_uint32_base(out, v, 0); +} + +/** + * Parse a uint32_t in binary encoding + */ +CALLBACK(parse_uint32_bin, bool, + uint32_t *out, chunk_t v) +{ + return parse_uint32_base(out, v, 2); +} + +/** * Parse a uint64_t */ CALLBACK(parse_uint64, bool, @@ -984,6 +1045,20 @@ CALLBACK(parse_tfc, bool, } /** + * Parse 6-bit DSCP value + */ +CALLBACK(parse_dscp, bool, + uint8_t *out, chunk_t v) +{ + if (parse_uint32_bin(out, v)) + { + *out = *out & 0x3f; + return TRUE; + } + return FALSE; +} + +/** * Parse authentication config */ CALLBACK(parse_auth, bool, @@ -1109,27 +1184,52 @@ CALLBACK(parse_group, bool, } /** - * Parse a certificate; add as auth rule to config + * Parse certificate policy */ -static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v) +CALLBACK(parse_cert_policy, bool, + auth_cfg_t *cfg, chunk_t v) +{ + char buf[BUF_LEN]; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + cfg->add(cfg, AUTH_RULE_CERT_POLICY, strdup(buf)); + return TRUE; +} + +/** + * Add a certificate as auth rule to config + */ +static bool add_cert(auth_data_t *auth, auth_rule_t rule, certificate_t *cert) { vici_authority_t *authority; vici_cred_t *cred; + + if (rule == AUTH_RULE_SUBJECT_CERT) + { + authority = auth->request->this->authority; + authority->check_for_hash_and_url(authority, cert); + } + cred = auth->request->this->cred; + cert = cred->add_cert(cred, cert); + auth->cfg->add(auth->cfg, rule, cert); + return TRUE; +} + +/** + * Parse a certificate; add as auth rule to config + */ +static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v) +{ certificate_t *cert; cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_BLOB_PEM, v, BUILD_END); if (cert) { - if (rule == AUTH_RULE_SUBJECT_CERT) - { - authority = auth->request->this->authority; - authority->check_for_hash_and_url(authority, cert); - } - cred = auth->request->this->cred; - cert = cred->add_cert(cred, cert); - auth->cfg->add(auth->cfg, rule, cert); - return TRUE; + return add_cert(auth, rule, cert); } return FALSE; } @@ -1314,6 +1414,38 @@ CALLBACK(parse_hosts, bool, return TRUE; } +#ifdef ME +/** + * Parse peer ID + */ +CALLBACK(parse_peer_id, bool, + identification_t **out, chunk_t v) +{ + char buf[BUF_LEN]; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + *out = identification_create_from_string(buf); + return TRUE; +} +#endif /* ME */ + +CALLBACK(cert_kv, bool, + cert_data_t *cert, vici_message_t *message, char *name, chunk_t value) +{ + parse_rule_t rules[] = { + { "handle", parse_string, &cert->handle }, + { "slot", parse_uint32, &cert->slot }, + { "module", parse_string, &cert->module }, + { "file", parse_string, &cert->file }, + }; + + return parse_rules(rules, countof(rules), name, value, + &cert->request->reply); +} + CALLBACK(child_li, bool, child_data_t *child, vici_message_t *message, char *name, chunk_t value) { @@ -1334,7 +1466,7 @@ CALLBACK(child_kv, bool, parse_rule_t rules[] = { { "updown", parse_string, &child->cfg.updown }, { "hostaccess", parse_bool, &child->cfg.hostaccess }, - { "mode", parse_mode, &child->cfg.mode }, + { "mode", parse_mode, &child->cfg }, { "policies", parse_bool, &child->policies }, { "policies_fwd_out", parse_bool, &child->policies_fwd_out }, { "replay_window", parse_uint32, &child->replay_window }, @@ -1369,6 +1501,7 @@ CALLBACK(auth_li, bool, { parse_rule_t rules[] = { { "groups", parse_group, auth->cfg }, + { "cert_policy", parse_cert_policy, auth }, { "certs", parse_certs, auth }, { "cacerts", parse_cacerts, auth }, { "pubkeys", parse_pubkeys, auth }, @@ -1417,6 +1550,7 @@ CALLBACK(peer_kv, bool, { "version", parse_uint32, &peer->version }, { "aggressive", parse_bool, &peer->aggressive }, { "pull", parse_bool, &peer->pull }, + { "dscp", parse_dscp, &peer->dscp }, { "encap", parse_bool, &peer->encap }, { "mobike", parse_bool, &peer->mobike }, { "dpd_delay", parse_time, &peer->dpd_delay }, @@ -1432,12 +1566,94 @@ CALLBACK(peer_kv, bool, { "rekey_time", parse_time, &peer->rekey_time }, { "over_time", parse_time, &peer->over_time }, { "rand_time", parse_time, &peer->rand_time }, +#ifdef ME + { "mediation", parse_bool, &peer->mediation }, + { "mediated_by", parse_string, &peer->mediated_by }, + { "mediation_peer", parse_peer_id, &peer->peer_id }, +#endif /* ME */ }; return parse_rules(rules, countof(rules), name, value, &peer->request->reply); } +CALLBACK(auth_sn, bool, + auth_data_t *auth, vici_message_t *message, vici_parse_context_t *ctx, + char *name) +{ + if (strcasepfx(name, "cert") || + strcasepfx(name, "cacert")) + { + cert_data_t *data; + auth_rule_t rule; + certificate_t *cert; + chunk_t handle; + + INIT(data, + .request = auth->request, + .slot = -1, + ); + + if (!message->parse(message, ctx, NULL, cert_kv, NULL, data)) + { + free_cert_data(data); + return FALSE; + } + if (!data->handle && !data->file) + { + auth->request->reply = create_reply("handle or file path missing: " + "%s", name); + free_cert_data(data); + return FALSE; + } + else if (data->handle && data->file) + { + auth->request->reply = create_reply("handle and file path given: " + "%s", name); + free_cert_data(data); + return FALSE; + } + + if (data->file) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, data->file, BUILD_END); + } + else + { + handle = chunk_from_hex(chunk_from_str(data->handle), NULL); + if (data->slot != -1) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, + CERT_X509, BUILD_PKCS11_KEYID, handle, + BUILD_PKCS11_SLOT, data->slot, + data->module ? BUILD_PKCS11_MODULE : BUILD_END, + data->module, BUILD_END); + } + else + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, + CERT_X509, BUILD_PKCS11_KEYID, handle, + data->module ? BUILD_PKCS11_MODULE : BUILD_END, + data->module, BUILD_END); + } + chunk_free(&handle); + } + free_cert_data(data); + if (!cert) + { + auth->request->reply = create_reply("unable to load certificate: " + "%s", name); + return FALSE; + } + rule = strcasepfx(name, "cert") ? AUTH_RULE_SUBJECT_CERT + : AUTH_RULE_CA_CERT; + return add_cert(auth, rule, cert); + } + auth->request->reply = create_reply("invalid section: %s", name); + return FALSE; +} + /** * Check and update lifetimes */ @@ -1600,7 +1816,7 @@ CALLBACK(peer_sn, bool, .cfg = auth_cfg_create(), ); - if (!message->parse(message, ctx, NULL, auth_kv, auth_li, auth)) + if (!message->parse(message, ctx, auth_sn, auth_kv, auth_li, auth)) { free_auth_data(auth); return FALSE; @@ -1703,7 +1919,8 @@ static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, { case MODE_PASS: case MODE_DROP: - charon->shunts->install(charon->shunts, child_cfg); + charon->shunts->install(charon->shunts, + peer_cfg->get_name(peer_cfg), child_cfg); break; default: charon->traps->install(charon->traps, peer_cfg, child_cfg, @@ -1724,6 +1941,7 @@ static void clear_start_action(private_vici_config_t *this, char *peer_name, { enumerator_t *enumerator, *children; child_sa_t *child_sa; + peer_cfg_t *peer_cfg; ike_sa_t *ike_sa; uint32_t id = 0, others; array_t *ids = NULL, *ikeids = NULL; @@ -1811,13 +2029,15 @@ static void clear_start_action(private_vici_config_t *this, char *peer_name, { case MODE_PASS: case MODE_DROP: - charon->shunts->uninstall(charon->shunts, name); + charon->shunts->uninstall(charon->shunts, peer_name, name); break; default: enumerator = charon->traps->create_enumerator(charon->traps); - while (enumerator->enumerate(enumerator, NULL, &child_sa)) + while (enumerator->enumerate(enumerator, &peer_cfg, + &child_sa)) { - if (streq(name, child_sa->get_name(child_sa))) + if (streq(peer_name, peer_cfg->get_name(peer_cfg)) && + streq(name, child_sa->get_name(child_sa))) { id = child_sa->get_reqid(child_sa); break; @@ -2080,12 +2300,48 @@ CALLBACK(config_sn, bool, peer.rand_time = min(peer.over_time, peer.rand_time / 2); } +#ifdef ME + if (peer.mediation && peer.mediated_by) + { + DBG1(DBG_CFG, "a mediation connection cannot be a mediated connection " + "at the same time, config discarded"); + free_peer_data(&peer); + return FALSE; + } + if (peer.mediation) + { /* force unique connections for mediation connections */ + peer.unique = UNIQUE_REPLACE; + } + else if (peer.mediated_by) + { /* fallback to remote identity of first auth round if peer_id is not + * given explicitly */ + auth_cfg_t *cfg; + + if (!peer.peer_id && + peer.remote->get_first(peer.remote, (void**)&cfg) == SUCCESS) + { + peer.peer_id = cfg->get(cfg, AUTH_RULE_IDENTITY); + if (peer.peer_id) + { + peer.peer_id = peer.peer_id->clone(peer.peer_id); + } + else + { + DBG1(DBG_CFG, "mediation peer missing for mediated connection, " + "config discarded"); + free_peer_data(&peer); + return FALSE; + } + } + } +#endif /* ME */ + log_peer_data(&peer); ike_cfg = ike_cfg_create(peer.version, peer.send_certreq, peer.encap, peer.local_addrs, peer.local_port, peer.remote_addrs, peer.remote_port, - peer.fragmentation, 0); + peer.fragmentation, peer.dscp); cfg = (peer_cfg_create_t){ .cert_policy = peer.send_cert, @@ -2101,6 +2357,14 @@ CALLBACK(config_sn, bool, .dpd = peer.dpd_delay, .dpd_timeout = peer.dpd_timeout, }; +#ifdef ME + cfg.mediation = peer.mediation; + if (peer.mediated_by) + { + cfg.mediated_by = peer.mediated_by; + cfg.peer_id = peer.peer_id->clone(peer.peer_id); + } +#endif /* ME */ peer_cfg = peer_cfg_create(name, ike_cfg, &cfg); while (peer.local->remove_first(peer.local, diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 59c214437..83e09d5b7 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2015 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2015-2017 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG @@ -23,6 +23,8 @@ #include <daemon.h> #include <collections/array.h> +#include <processing/jobs/rekey_ike_sa_job.h> +#include <processing/jobs/rekey_child_sa_job.h> #include <processing/jobs/redirect_job.h> typedef struct private_vici_control_t private_vici_control_t; @@ -360,6 +362,100 @@ CALLBACK(terminate, vici_message_t*, return builder->finalize(builder); } +CALLBACK(rekey, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *isas, *csas; + char *child, *ike, *errmsg = NULL; + u_int child_id, ike_id, found = 0; + ike_sa_t *ike_sa; + child_sa_t *child_sa; + vici_builder_t *builder; + + child = request->get_str(request, NULL, "child"); + ike = request->get_str(request, NULL, "ike"); + child_id = request->get_int(request, 0, "child-id"); + ike_id = request->get_int(request, 0, "ike-id"); + + if (!child && !ike && !ike_id && !child_id) + { + return send_reply(this, "missing rekey selector"); + } + + if (ike_id) + { + DBG1(DBG_CFG, "vici rekey IKE_SA #%d", ike_id); + } + if (child_id) + { + DBG1(DBG_CFG, "vici rekey CHILD_SA #%d", child_id); + } + if (ike) + { + DBG1(DBG_CFG, "vici rekey IKE_SA '%s'", ike); + } + if (child) + { + DBG1(DBG_CFG, "vici rekey CHILD_SA '%s'", child); + } + + isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); + while (isas->enumerate(isas, &ike_sa)) + { + if (child || child_id) + { + if (ike && !streq(ike, ike_sa->get_name(ike_sa))) + { + continue; + } + if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa)) + { + continue; + } + csas = ike_sa->create_child_sa_enumerator(ike_sa); + while (csas->enumerate(csas, &child_sa)) + { + if (child && !streq(child, child_sa->get_name(child_sa))) + { + continue; + } + if (child_id && child_sa->get_unique_id(child_sa) != child_id) + { + continue; + } + lib->processor->queue_job(lib->processor, + (job_t*)rekey_child_sa_job_create( + child_sa->get_protocol(child_sa), + child_sa->get_spi(child_sa, TRUE), + ike_sa->get_my_host(ike_sa))); + found++; + } + csas->destroy(csas); + } + else if ((ike && streq(ike, ike_sa->get_name(ike_sa))) || + (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))) + { + lib->processor->queue_job(lib->processor, + (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE)); + found++; + } + } + isas->destroy(isas); + + builder = vici_builder_create(); + if (!found) + { + errmsg = "no matching SAs to rekey found"; + } + builder->add_kv(builder, "success", errmsg ? "no" : "yes"); + builder->add_kv(builder, "matches", "%u", found); + if (errmsg) + { + builder->add_kv(builder, "errmsg", "%s", errmsg); + } + return builder->finalize(builder); +} + /** * Parse a peer-ip specified, which can be a subnet in CIDR notation, a range * or a single IP address. @@ -494,6 +590,7 @@ CALLBACK(redirect, vici_message_t*, errmsg = "no matching SAs to redirect found"; } builder->add_kv(builder, "success", errmsg ? "no" : "yes"); + builder->add_kv(builder, "matches", "%u", found); if (errmsg) { builder->add_kv(builder, "errmsg", "%s", errmsg); @@ -565,7 +662,8 @@ CALLBACK(install, vici_message_t*, { case MODE_PASS: case MODE_DROP: - ok = charon->shunts->install(charon->shunts, child_cfg); + ok = charon->shunts->install(charon->shunts, + peer_cfg->get_name(peer_cfg), child_cfg); break; default: ok = charon->traps->install(charon->traps, peer_cfg, child_cfg, @@ -581,12 +679,15 @@ CALLBACK(install, vici_message_t*, CALLBACK(uninstall, vici_message_t*, private_vici_control_t *this, char *name, u_int id, vici_message_t *request) { + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg; child_sa_t *child_sa; enumerator_t *enumerator; uint32_t reqid = 0; - char *child; + char *child, *ike, *ns; child = request->get_str(request, NULL, "child"); + ike = request->get_str(request, NULL, "ike"); if (!child) { return send_reply(this, "missing configuration name"); @@ -594,15 +695,35 @@ CALLBACK(uninstall, vici_message_t*, DBG1(DBG_CFG, "vici uninstall '%s'", child); - if (charon->shunts->uninstall(charon->shunts, child)) + if (!ike) + { + enumerator = charon->shunts->create_enumerator(charon->shunts); + while (enumerator->enumerate(enumerator, &ns, &child_cfg)) + { + if (ns && streq(child, child_cfg->get_name(child_cfg))) + { + ike = strdup(ns); + break; + } + } + enumerator->destroy(enumerator); + if (ike && charon->shunts->uninstall(charon->shunts, ike, child)) + { + free(ike); + return send_reply(this, NULL); + } + free(ike); + } + else if (charon->shunts->uninstall(charon->shunts, ike, child)) { return send_reply(this, NULL); } enumerator = charon->traps->create_enumerator(charon->traps); - while (enumerator->enumerate(enumerator, NULL, &child_sa)) + while (enumerator->enumerate(enumerator, &peer_cfg, &child_sa)) { - if (streq(child, child_sa->get_name(child_sa))) + if ((!ike || streq(ike, peer_cfg->get_name(peer_cfg))) && + streq(child, child_sa->get_name(child_sa))) { reqid = child_sa->get_reqid(child_sa); break; @@ -647,6 +768,7 @@ static void manage_commands(private_vici_control_t *this, bool reg) { manage_command(this, "initiate", initiate, reg); manage_command(this, "terminate", terminate, reg); + manage_command(this, "rekey", rekey, reg); manage_command(this, "redirect", redirect, reg); manage_command(this, "install", install, reg); manage_command(this, "uninstall", uninstall, reg); diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index baf285fb8..6c7c194c2 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -1,9 +1,11 @@ /* + * Copyright (C) 2015-2016 Andreas Steffen + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * - * Copyright (C) 2015-2016 Andreas Steffen - * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -55,6 +57,11 @@ struct private_vici_cred_t { mem_cred_t *creds; /** + * separate credential set for token PINs + */ + mem_cred_t *pins; + + /** * cache CRLs to disk? */ bool cachecrl; @@ -249,6 +256,139 @@ CALLBACK(load_key, vici_message_t*, return create_reply(NULL); } +CALLBACK(unload_key, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + chunk_t keyid; + char buf[BUF_LEN], *hex, *msg = NULL; + + hex = message->get_str(message, NULL, "id"); + if (!hex) + { + return create_reply("key id missing"); + } + keyid = chunk_from_hex(chunk_from_str(hex), NULL); + snprintf(buf, sizeof(buf), "%+B", &keyid); + DBG1(DBG_CFG, "unloaded private key with id %s", buf); + if (this->creds->remove_key(this->creds, keyid)) + { /* also remove any potential PIN associated with this id */ + this->pins->remove_shared_unique(this->pins, buf); + } + else + { + msg = "key not found"; + } + chunk_free(&keyid); + return create_reply(msg); +} + +CALLBACK(get_keys, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + vici_builder_t *builder; + enumerator_t *enumerator; + private_key_t *private; + chunk_t keyid; + + builder = vici_builder_create(); + builder->begin_list(builder, "keys"); + + enumerator = this->creds->set.create_private_enumerator(&this->creds->set, + KEY_ANY, NULL); + while (enumerator->enumerate(enumerator, &private)) + { + if (private->get_fingerprint(private, KEYID_PUBKEY_SHA1, &keyid)) + { + builder->add_li(builder, "%+B", &keyid); + } + } + enumerator->destroy(enumerator); + + builder->end_list(builder); + return builder->finalize(builder); +} + +CALLBACK(load_token, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + vici_builder_t *builder; + private_key_t *key; + shared_key_t *shared = NULL; + identification_t *owner; + mem_cred_t *set = NULL; + chunk_t handle, fp; + char buf[BUF_LEN], *hex, *module, *pin, *unique = NULL; + int slot; + + hex = message->get_str(message, NULL, "handle"); + if (!hex) + { + return create_reply("keyid missing"); + } + handle = chunk_from_hex(chunk_from_str(hex), NULL); + slot = message->get_int(message, -1, "slot"); + module = message->get_str(message, NULL, "module"); + pin = message->get_str(message, NULL, "pin"); + + if (pin) + { /* provide the pin in a temporary credential set to access the key */ + shared = shared_key_create(SHARED_PIN, chunk_clone(chunk_from_str(pin))); + owner = identification_create_from_encoding(ID_KEY_ID, handle); + set = mem_cred_create(); + set->add_shared(set, shared->get_ref(shared), owner, NULL); + lib->credmgr->add_local_set(lib->credmgr, &set->set, FALSE); + } + if (slot >= 0) + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY, + BUILD_PKCS11_KEYID, handle, + BUILD_PKCS11_SLOT, slot, + module ? BUILD_PKCS11_MODULE : BUILD_END, module, + BUILD_END); + } + else + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY, + BUILD_PKCS11_KEYID, handle, + module ? BUILD_PKCS11_MODULE : BUILD_END, module, + BUILD_END); + } + if (set) + { + lib->credmgr->remove_local_set(lib->credmgr, &set->set); + set->destroy(set); + } + if (!key) + { + chunk_free(&handle); + DESTROY_IF(shared); + return create_reply("loading private key from token failed"); + } + builder = vici_builder_create(); + builder->add_kv(builder, "success", "yes"); + if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &fp)) + { + snprintf(buf, sizeof(buf), "%+B", &fp); + builder->add_kv(builder, "id", "%s", buf); + unique = buf; + } + if (shared && unique) + { /* use the handle as owner, but the key identifier as unique ID */ + owner = identification_create_from_encoding(ID_KEY_ID, handle); + this->pins->add_shared_unique(this->pins, unique, shared, + linked_list_create_with_items(owner, NULL)); + } + else + { + DESTROY_IF(shared); + } + DBG1(DBG_CFG, "loaded %N private key from token", key_type_names, + key->get_type(key)); + this->creds->add_key(this->creds, key); + chunk_free(&handle); + return builder->finalize(builder); +} + CALLBACK(shared_owners, bool, linked_list_t *owners, vici_message_t *message, char *name, chunk_t value) { @@ -271,11 +411,12 @@ CALLBACK(load_shared, vici_message_t*, shared_key_type_t type; linked_list_t *owners; chunk_t data; - char *str, buf[512] = ""; + char *unique, *str, buf[512] = ""; enumerator_t *enumerator; identification_t *owner; int len; + unique = message->get_str(message, NULL, "id"); str = message->get_str(message, NULL, "type"); if (!str) { @@ -289,6 +430,10 @@ CALLBACK(load_shared, vici_message_t*, { type = SHARED_EAP; } + else if (strcaseeq(str, "ntlm")) + { + type = SHARED_NT_HASH; + } else { return create_reply("invalid shared key type: %s", str); @@ -322,15 +467,59 @@ CALLBACK(load_shared, vici_message_t*, } enumerator->destroy(enumerator); - DBG1(DBG_CFG, "loaded %N shared key for: %s", - shared_key_type_names, type, buf); + if (unique) + { + DBG1(DBG_CFG, "loaded %N shared key with id '%s' for: %s", + shared_key_type_names, type, unique, buf); + } + else + { + DBG1(DBG_CFG, "loaded %N shared key for: %s", + shared_key_type_names, type, buf); + } - this->creds->add_shared_list(this->creds, + this->creds->add_shared_unique(this->creds, unique, shared_key_create(type, chunk_clone(data)), owners); return create_reply(NULL); } +CALLBACK(unload_shared, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + char *unique; + + unique = message->get_str(message, NULL, "id"); + if (!unique) + { + return create_reply("unique identifier missing"); + } + DBG1(DBG_CFG, "unloaded shared key with id '%s'", unique); + this->creds->remove_shared_unique(this->creds, unique); + return create_reply(NULL); +} + +CALLBACK(get_shared, vici_message_t*, + private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) +{ + vici_builder_t *builder; + enumerator_t *enumerator; + char *unique; + + builder = vici_builder_create(); + builder->begin_list(builder, "keys"); + + enumerator = this->creds->create_unique_shared_enumerator(this->creds); + while (enumerator->enumerate(enumerator, &unique)) + { + builder->add_li(builder, "%s", unique); + } + enumerator->destroy(enumerator); + + builder->end_list(builder); + return builder->finalize(builder); +} + CALLBACK(clear_creds, vici_message_t*, private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) { @@ -374,7 +563,12 @@ static void manage_commands(private_vici_cred_t *this, bool reg) manage_command(this, "flush-certs", flush_certs, reg); manage_command(this, "load-cert", load_cert, reg); manage_command(this, "load-key", load_key, reg); + manage_command(this, "unload-key", unload_key, reg); + manage_command(this, "get-keys", get_keys, reg); + manage_command(this, "load-token", load_token, reg); manage_command(this, "load-shared", load_shared, reg); + manage_command(this, "unload-shared", unload_shared, reg); + manage_command(this, "get-shared", get_shared, reg); } METHOD(vici_cred_t, add_cert, certificate_t*, @@ -390,6 +584,8 @@ METHOD(vici_cred_t, destroy, void, lib->credmgr->remove_set(lib->credmgr, &this->creds->set); this->creds->destroy(this->creds); + lib->credmgr->remove_set(lib->credmgr, &this->pins->set); + this->pins->destroy(this->pins); free(this); } @@ -414,6 +610,7 @@ vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher) }, .dispatcher = dispatcher, .creds = mem_cred_create(), + .pins = mem_cred_create(), ); if (lib->settings->get_bool(lib->settings, "%s.cache_crls", FALSE, lib->ns)) @@ -422,6 +619,7 @@ vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher) DBG1(DBG_CFG, "crl caching to %s enabled", CRL_DIR); } lib->credmgr->add_set(lib->credmgr, &this->creds->set); + lib->credmgr->add_set(lib->credmgr, &this->pins->set); manage_commands(this, TRUE); diff --git a/src/libcharon/plugins/vici/vici_dispatcher.c b/src/libcharon/plugins/vici/vici_dispatcher.c index ffe0d61e5..596255b91 100644 --- a/src/libcharon/plugins/vici/vici_dispatcher.c +++ b/src/libcharon/plugins/vici/vici_dispatcher.c @@ -471,15 +471,17 @@ METHOD(vici_dispatcher_t, manage_event, void, METHOD(vici_dispatcher_t, has_event_listeners, bool, private_vici_dispatcher_t *this, char *name) { + event_t *event; bool retval = FALSE; this->mutex->lock(this->mutex); - if (this->events->get(this->events, name)) + event = this->events->get(this->events, name); + if (event) { /* the entry might be getting destroyed, but returning * false positive is not a problem as a later raise_event * will check things again. */ - retval = TRUE; + retval = array_count(event->clients); } this->mutex->unlock(this->mutex); diff --git a/src/libcharon/plugins/vici/vici_logger.c b/src/libcharon/plugins/vici/vici_logger.c index 6d3584ebd..8e7bcfa1c 100644 --- a/src/libcharon/plugins/vici/vici_logger.c +++ b/src/libcharon/plugins/vici/vici_logger.c @@ -95,6 +95,11 @@ METHOD(logger_t, log_, void, private_vici_logger_t *this, debug_t group, level_t level, int thread, ike_sa_t* ike_sa, const char *msg) { + if (!this->dispatcher->has_event_listeners(this->dispatcher, "log")) + { + return; + } + this->mutex->lock(this->mutex); /* avoid recursive invocations by the vici subsystem */ @@ -130,6 +135,8 @@ METHOD(logger_t, log_, void, METHOD(logger_t, get_level, level_t, private_vici_logger_t *this, debug_t group) { + /* anything higher might produce a loop as sending messages or listening + * for clients might cause log messages itself */ return LEVEL_CTRL; } diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index e3a16f5ea..c0f4e2de9 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -93,6 +93,29 @@ static void add_mark(vici_builder_t *b, mark_t mark, } /** + * List the mode of a CHILD_SA or config + */ +static void list_mode(vici_builder_t *b, child_sa_t *child, child_cfg_t *cfg) +{ + ipsec_mode_t mode; + char *sub_mode = ""; + + if (child || cfg) + { + if (!cfg) + { + cfg = child->get_config(child); + } + mode = child ? child->get_mode(child) : cfg->get_mode(cfg); + if (mode == MODE_TRANSPORT && cfg->use_proxy_mode(cfg)) + { /* only report this if the negotiated mode is actually TRANSPORT */ + sub_mode = "_PROXY"; + } + b->add_kv(b, "mode", "%N%s", ipsec_mode_names, mode, sub_mode); + } +} + +/** * List details of a CHILD_SA */ static void list_child(private_vici_query_t *this, vici_builder_t *b, @@ -105,10 +128,11 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, enumerator_t *enumerator; traffic_selector_t *ts; + b->add_kv(b, "name", "%s", child->get_name(child)); b->add_kv(b, "uniqueid", "%u", child->get_unique_id(child)); b->add_kv(b, "reqid", "%u", child->get_reqid(child)); b->add_kv(b, "state", "%N", child_sa_state_names, child->get_state(child)); - b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child)); + list_mode(b, child, NULL); if (child->get_state(child) == CHILD_INSTALLED || child->get_state(child) == CHILD_REKEYING || child->get_state(child) == CHILD_REKEYED) @@ -397,6 +421,7 @@ CALLBACK(list_sas, vici_message_t*, char *ike; u_int ike_id; bool bl; + char buf[BUF_LEN]; bl = request->get_str(request, NULL, "noblock") == NULL; ike = request->get_str(request, NULL, "ike"); @@ -425,7 +450,9 @@ CALLBACK(list_sas, vici_message_t*, csas = ike_sa->create_child_sa_enumerator(ike_sa); while (csas->enumerate(csas, &child_sa)) { - b->begin_section(b, child_sa->get_name(child_sa)); + snprintf(buf, sizeof(buf), "%s-%u", child_sa->get_name(child_sa), + child_sa->get_unique_id(child_sa)); + b->begin_section(b, buf); list_child(this, b, child_sa, now); b->end_section(b); } @@ -446,16 +473,21 @@ CALLBACK(list_sas, vici_message_t*, /** * Raise a list-policy event for given CHILD_SA */ -static void raise_policy(private_vici_query_t *this, u_int id, child_sa_t *child) +static void raise_policy(private_vici_query_t *this, u_int id, char *ike, + child_sa_t *child) { enumerator_t *enumerator; traffic_selector_t *ts; vici_builder_t *b; + char buf[BUF_LEN]; b = vici_builder_create(); - b->begin_section(b, child->get_name(child)); + snprintf(buf, sizeof(buf), "%s/%s", ike, child->get_name(child)); + b->begin_section(b, buf); + b->add_kv(b, "child", "%s", child->get_name(child)); + b->add_kv(b, "ike", "%s", ike); - b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child)); + list_mode(b, child, NULL); b->begin_list(b, "local-ts"); enumerator = child->create_ts_enumerator(child, TRUE); @@ -484,18 +516,26 @@ static void raise_policy(private_vici_query_t *this, u_int id, child_sa_t *child /** * Raise a list-policy event for given CHILD_SA config */ -static void raise_policy_cfg(private_vici_query_t *this, u_int id, +static void raise_policy_cfg(private_vici_query_t *this, u_int id, char *ike, child_cfg_t *cfg) { enumerator_t *enumerator; linked_list_t *list; traffic_selector_t *ts; vici_builder_t *b; + char buf[BUF_LEN]; b = vici_builder_create(); - b->begin_section(b, cfg->get_name(cfg)); + snprintf(buf, sizeof(buf), "%s%s%s", ike ? ike : "", ike ? "/" : "", + cfg->get_name(cfg)); + b->begin_section(b, buf); + b->add_kv(b, "child", "%s", cfg->get_name(cfg)); + if (ike) + { + b->add_kv(b, "ike", "%s", ike); + } - b->add_kv(b, "mode", "%N", ipsec_mode_names, cfg->get_mode(cfg)); + list_mode(b, NULL, cfg); b->begin_list(b, "local-ts"); list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL); @@ -531,25 +571,28 @@ CALLBACK(list_policies, vici_message_t*, enumerator_t *enumerator; vici_builder_t *b; child_sa_t *child_sa; + peer_cfg_t *peer_cfg; child_cfg_t *child_cfg; bool drop, pass, trap; - char *child; + char *child, *ike, *ns; drop = request->get_str(request, NULL, "drop") != NULL; pass = request->get_str(request, NULL, "pass") != NULL; trap = request->get_str(request, NULL, "trap") != NULL; child = request->get_str(request, NULL, "child"); + ike = request->get_str(request, NULL, "ike"); if (trap) { enumerator = charon->traps->create_enumerator(charon->traps); - while (enumerator->enumerate(enumerator, NULL, &child_sa)) + while (enumerator->enumerate(enumerator, &peer_cfg, &child_sa)) { - if (child && !streq(child, child_sa->get_name(child_sa))) + if ((ike && !streq(ike, peer_cfg->get_name(peer_cfg))) || + (child && !streq(child, child_sa->get_name(child_sa)))) { continue; } - raise_policy(this, id, child_sa); + raise_policy(this, id, peer_cfg->get_name(peer_cfg), child_sa); } enumerator->destroy(enumerator); } @@ -557,9 +600,10 @@ CALLBACK(list_policies, vici_message_t*, if (drop || pass) { enumerator = charon->shunts->create_enumerator(charon->shunts); - while (enumerator->enumerate(enumerator, &child_cfg)) + while (enumerator->enumerate(enumerator, &ns, &child_cfg)) { - if (child && !streq(child, child_cfg->get_name(child_cfg))) + if ((ike && !streq(ike, ns)) || + (child && !streq(child, child_cfg->get_name(child_cfg)))) { continue; } @@ -568,13 +612,13 @@ CALLBACK(list_policies, vici_message_t*, case MODE_DROP: if (drop) { - raise_policy_cfg(this, id, child_cfg); + raise_policy_cfg(this, id, ns, child_cfg); } break; case MODE_PASS: if (pass) { - raise_policy_cfg(this, id, child_cfg); + raise_policy_cfg(this, id, ns, child_cfg); } break; default: @@ -746,6 +790,8 @@ CALLBACK(list_conns, vici_message_t*, peer_cfg->get_reauth_time(peer_cfg, FALSE)); b->add_kv(b, "rekey_time", "%u", peer_cfg->get_rekey_time(peer_cfg, FALSE)); + b->add_kv(b, "unique", "%N", unique_policy_names, + peer_cfg->get_unique_policy(peer_cfg)); build_auth_cfgs(peer_cfg, TRUE, b); build_auth_cfgs(peer_cfg, FALSE, b); @@ -757,8 +803,7 @@ CALLBACK(list_conns, vici_message_t*, { b->begin_section(b, child_cfg->get_name(child_cfg)); - b->add_kv(b, "mode", "%N", ipsec_mode_names, - child_cfg->get_mode(child_cfg)); + list_mode(b, NULL, child_cfg); lft = child_cfg->get_lifetime(child_cfg, FALSE); b->add_kv(b, "rekey_time", "%"PRIu64, lft->time.rekey); |
