diff options
Diffstat (limited to 'src/libcharon/plugins/vici')
-rw-r--r-- | src/libcharon/plugins/vici/Makefile.in | 2 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/README.md | 109 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/perl/Makefile.in | 2 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/python/Makefile.in | 2 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/python/vici/protocol.py | 13 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/python/vici/session.py | 6 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/ruby/Makefile.in | 2 | ||||
-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_config.h | 2 | ||||
-rw-r--r-- | src/libcharon/plugins/vici/vici_control.c | 141 | ||||
-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 | 96 |
17 files changed, 948 insertions, 99 deletions
diff --git a/src/libcharon/plugins/vici/Makefile.in b/src/libcharon/plugins/vici/Makefile.in index ce1520424..cdefbff79 100644 --- a/src/libcharon/plugins/vici/Makefile.in +++ b/src/libcharon/plugins/vici/Makefile.in @@ -456,7 +456,6 @@ exec_prefix = @exec_prefix@ fips_mode = @fips_mode@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ -h_plugins = @h_plugins@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -491,6 +490,7 @@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ +p_plugins = @p_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 18a3ef7b5..9bda949d0 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -1,8 +1,8 @@ # The Versatile IKE Control Interface (VICI) protocol # -The vici plugin implements the server side of an IPC protocol to configure, -monitor and control the IKE daemon charon. It uses request/response and event -messages to communicate over a reliable stream based transport. +The vici _[ˈvitʃi]_ plugin implements the server side of an IPC protocol to +configure, monitor and control the IKE daemon charon. It uses request/response +and event messages to communicate over a reliable stream based transport. ## Transport protocol ## @@ -258,7 +258,7 @@ Initiates an SA while streaming _control-log_ events. { child = <CHILD_SA configuration name to initiate> - ike = <optional IKE_SA configuraiton name to find child under> + ike = <optional IKE_SA configuration name to find child under> timeout = <timeout in ms before returning> init-limits = <whether limits may prevent initiating the CHILD_SA> loglevel = <loglevel to issue "control-log" events for> @@ -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> @@ -689,6 +776,10 @@ command. spi-out = <hex encoded outbound SPI> cpi-in = <hex encoded inbound CPI, if using compression> cpi-out = <hex encoded outbound CPI, if using compression> + mark-in = <hex encoded inbound Netfilter mark value> + mark-mask-in = <hex encoded inbound Netfilter mark mask> + mark-out = <hex encoded outbound Netfilter mark value> + mark-mask-out = <hex encoded outbound Netfilter mark mask> encr-alg = <ESP encryption algorithm name, if any> encr-keysize = <ESP encryption key size, if applicable> integ-alg = <ESP or AH integrity algorithm name, if any> @@ -722,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/perl/Makefile.in b/src/libcharon/plugins/vici/perl/Makefile.in index 523868c68..385aa9775 100644 --- a/src/libcharon/plugins/vici/perl/Makefile.in +++ b/src/libcharon/plugins/vici/perl/Makefile.in @@ -274,7 +274,6 @@ exec_prefix = @exec_prefix@ fips_mode = @fips_mode@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ -h_plugins = @h_plugins@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -309,6 +308,7 @@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ +p_plugins = @p_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ diff --git a/src/libcharon/plugins/vici/python/Makefile.in b/src/libcharon/plugins/vici/python/Makefile.in index 4f1a91703..f783d7068 100644 --- a/src/libcharon/plugins/vici/python/Makefile.in +++ b/src/libcharon/plugins/vici/python/Makefile.in @@ -296,7 +296,6 @@ exec_prefix = @exec_prefix@ fips_mode = @fips_mode@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ -h_plugins = @h_plugins@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -331,6 +330,7 @@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ +p_plugins = @p_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ diff --git a/src/libcharon/plugins/vici/python/vici/protocol.py b/src/libcharon/plugins/vici/python/vici/protocol.py index 4951817eb..919231d43 100644 --- a/src/libcharon/plugins/vici/python/vici/protocol.py +++ b/src/libcharon/plugins/vici/python/vici/protocol.py @@ -33,7 +33,10 @@ class Transport(object): """Ensure to read count bytes from the socket""" data = b"" while len(data) < count: - data += self.socket.recv(count - len(data)) + buf = self.socket.recv(count - len(data)) + if not buf: + raise socket.error('Connection closed') + data += buf return data @@ -59,7 +62,7 @@ class Packet(object): @classmethod def _named_request(cls, request_type, request, message=None): - request = request.encode() + requestdata = request.encode("UTF-8") payload = struct.pack("!BB", request_type, len(request)) + request if message is not None: return payload + message @@ -102,12 +105,12 @@ class Message(object): @classmethod def serialize(cls, message): def encode_named_type(marker, name): - name = name.encode() + name = name.encode("UTF-8") return struct.pack("!BB", marker, len(name)) + name def encode_blob(value): if not isinstance(value, bytes): - value = str(value).encode() + value = str(value).encode("UTF-8") return struct.pack("!H", len(value)) + value def serialize_list(lst): @@ -144,7 +147,7 @@ class Message(object): def deserialize(cls, stream): def decode_named_type(stream): length, = struct.unpack("!B", stream.read(1)) - return stream.read(length).decode() + return stream.read(length).decode("UTF-8") def decode_blob(stream): length, = struct.unpack("!H", stream.read(2)) 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/Makefile.in b/src/libcharon/plugins/vici/ruby/Makefile.in index e176285a8..125f44ee1 100644 --- a/src/libcharon/plugins/vici/ruby/Makefile.in +++ b/src/libcharon/plugins/vici/ruby/Makefile.in @@ -274,7 +274,6 @@ exec_prefix = @exec_prefix@ fips_mode = @fips_mode@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ -h_plugins = @h_plugins@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -309,6 +308,7 @@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ +p_plugins = @p_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ 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_config.h b/src/libcharon/plugins/vici/vici_config.h index 0c237e7de..6bff41c31 100644 --- a/src/libcharon/plugins/vici/vici_config.h +++ b/src/libcharon/plugins/vici/vici_config.h @@ -38,7 +38,7 @@ typedef struct vici_config_t vici_config_t; struct vici_config_t { /** - * Implements a configuraiton backend. + * Implements a configuration backend. */ backend_t backend; diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 44003819a..afee649f7 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,39 @@ 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) + { + if (charon->shunts->uninstall(charon->shunts, ike, child)) + { + free(ike); + return send_reply(this, NULL); + } + free(ike); + return send_reply(this, "uninstalling policy '%s' failed", child); + } + } + 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; @@ -626,6 +751,7 @@ CALLBACK(reload_settings, vici_message_t*, { if (lib->settings->load_files(lib->settings, lib->conf, FALSE)) { + charon->load_loggers(charon); lib->plugins->reload(lib->plugins, NULL); return send_reply(this, NULL); } @@ -646,6 +772,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 828b61927..c0f4e2de9 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -79,6 +79,42 @@ struct private_vici_query_t { time_t uptime; }; +static void add_mark(vici_builder_t *b, mark_t mark, + char *label, char *mask_label) +{ + if (mark.value | mark.mask) + { + b->add_kv(b, label, "%.8x", mark.value); + if (~mark.mask) + { + b->add_kv(b, mask_label, "%.8x", mark.mask); + } + } +} + +/** + * 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 */ @@ -92,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) @@ -114,6 +151,8 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, b->add_kv(b, "cpi-in", "%.4x", ntohs(child->get_cpi(child, TRUE))); b->add_kv(b, "cpi-out", "%.4x", ntohs(child->get_cpi(child, FALSE))); } + add_mark(b, child->get_mark(child, TRUE), "mark-in", "mark-mask-in"); + add_mark(b, child->get_mark(child, FALSE), "mark-out", "mark-mask-out"); proposal = child->get_proposal(child); if (proposal) { @@ -382,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"); @@ -410,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); } @@ -431,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); @@ -469,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); @@ -516,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); } @@ -542,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; } @@ -553,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: @@ -731,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); @@ -742,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); |