From 71fa1224ec9b227d4aa8f1350d132380af00e786 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 10:18:01 +0100 Subject: [PATCH 01/33] vici: Add option to query a specific pool --- src/libcharon/plugins/vici/README.md | 1 + src/libcharon/plugins/vici/vici_attribute.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 4e53d7cc9..be0eff5c5 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -569,6 +569,7 @@ List the currently loaded pools. { leases = + name = } => { * = { base = 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); From d20bf50e04b5ed34f045cf084a4ad14f6026891e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 8 Dec 2016 18:14:40 +0100 Subject: [PATCH 02/33] vici: Update get_pools() in Python and Ruby bindings --- src/libcharon/plugins/vici/python/vici/session.py | 6 ++++-- src/libcharon/plugins/vici/ruby/lib/vici.rb | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) 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 ## From 257f6cb8e7b7b7f528c01950e5b5a8db01b153e5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 10:37:56 +0100 Subject: [PATCH 03/33] swanctl: Add possibility to query a specific pool by name --- src/swanctl/commands/list_pools.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/swanctl/commands/list_pools.c b/src/swanctl/commands/list_pools.c index 429107e17..a170adeba 100644 --- a/src/swanctl/commands/list_pools.c +++ b/src/swanctl/commands/list_pools.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2015 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2015-2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG @@ -58,7 +58,7 @@ static int list_pools(vici_conn_t *conn) vici_req_t *req; vici_res_t *res; command_format_options_t format = COMMAND_FORMAT_NONE; - char *arg; + char *arg, *name = NULL; int ret = 0; bool leases = FALSE; @@ -77,6 +77,9 @@ static int list_pools(vici_conn_t *conn) case 'l': leases = TRUE; continue; + case 'n': + name = arg; + continue; case EOF: break; default: @@ -90,6 +93,10 @@ static int list_pools(vici_conn_t *conn) { vici_add_key_valuef(req, "leases", "yes"); } + if (name) + { + vici_add_key_valuef(req, "name", "%s", name); + } res = vici_submit(req, conn); if (!res) { @@ -123,6 +130,7 @@ static void __attribute__ ((constructor))reg() {"raw", 'r', 0, "dump raw response message"}, {"pretty", 'P', 0, "dump raw response message in pretty print"}, {"leases", 'l', 0, "list leases of each pool"}, + {"name", 'n', 1, "filter pools by name"}, } }); } From dd5ee9d415837a51525b950e57eb01705de33c07 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 11:22:11 +0100 Subject: [PATCH 04/33] mem-cred: Add method to remove a private key with a specific fingerprint --- src/libstrongswan/credentials/sets/mem_cred.c | 27 +++++++++++++++++++ src/libstrongswan/credentials/sets/mem_cred.h | 13 +++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/credentials/sets/mem_cred.c b/src/libstrongswan/credentials/sets/mem_cred.c index 0f8bff23f..110986f1a 100644 --- a/src/libstrongswan/credentials/sets/mem_cred.c +++ b/src/libstrongswan/credentials/sets/mem_cred.c @@ -370,6 +370,32 @@ METHOD(mem_cred_t, add_key, void, this->lock->unlock(this->lock); } +METHOD(mem_cred_t, remove_key, bool, + private_mem_cred_t *this, chunk_t fp) +{ + enumerator_t *enumerator; + private_key_t *current; + bool found = FALSE; + + this->lock->write_lock(this->lock); + + enumerator = this->keys->create_enumerator(this->keys); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (current->has_fingerprint(current, fp)) + { + this->keys->remove_at(this->keys, enumerator); + current->destroy(current); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + this->lock->unlock(this->lock); + return found; +} + /** * Shared key entry */ @@ -817,6 +843,7 @@ mem_cred_t *mem_cred_create() .get_cert_ref = _get_cert_ref, .add_crl = _add_crl, .add_key = _add_key, + .remove_key = _remove_key, .add_shared = _add_shared, .add_shared_list = _add_shared_list, .add_cdp = _add_cdp, diff --git a/src/libstrongswan/credentials/sets/mem_cred.h b/src/libstrongswan/credentials/sets/mem_cred.h index 51f0b8c30..ac125d4e8 100644 --- a/src/libstrongswan/credentials/sets/mem_cred.h +++ b/src/libstrongswan/credentials/sets/mem_cred.h @@ -1,6 +1,7 @@ /* - * Copyright (C) 2010-2015 Tobias Brunner - * Hochschule fuer Technik Rapperswil + * Copyright (C) 2010-2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -86,6 +87,14 @@ struct mem_cred_t { */ void (*add_key)(mem_cred_t *this, private_key_t *key); + /** + * Remove a private key from the credential set. + * + * @param fp fingerprint of the key to remove + * @return TRUE if the key was found and removed + */ + bool (*remove_key)(mem_cred_t *this, chunk_t fp); + /** * Add a shared key to the credential set. * From 2a56acf501791be3c5a9849286de636648cd18ff Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 11:49:32 +0100 Subject: [PATCH 05/33] vici: Add commands to enumerate and remove private keys They are identified by their SHA-1 key identifier. --- src/libcharon/plugins/vici/README.md | 22 +++++++++++ src/libcharon/plugins/vici/vici_cred.c | 55 +++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index be0eff5c5..a46c35a28 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -466,6 +466,28 @@ Load a private key into the daemon. errmsg = } +### unload-key() ### + +Unload the private key with the given key identifier. + + { + id = + } => { + success = + errmsg = + } + +### get-keys() ### + +Return a list of identifiers of private keys loaded exclusively over vici, not +including keys found in other backends. + + {} => { + keys = [ + + ] + } + ### load-shared() ### Load a shared IKE PSK, EAP or XAuth secret into the daemon. diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index baf285fb8..03649acfe 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 @@ -249,6 +251,53 @@ 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 *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); + DBG1(DBG_CFG, "unloaded private key with id %+B", &keyid); + if (!this->creds->remove_key(this->creds, keyid)) + { + 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(shared_owners, bool, linked_list_t *owners, vici_message_t *message, char *name, chunk_t value) { @@ -374,6 +423,8 @@ 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-shared", load_shared, reg); } From 04180409ada6da511c966c6e34e4d69106eb21bf Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 12:25:00 +0100 Subject: [PATCH 06/33] swanctl: Automatically unload removed private keys --- src/swanctl/commands/load_creds.c | 251 +++++++++++++++++++++--------- 1 file changed, 175 insertions(+), 76 deletions(-) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 6278f66b4..6c084e5e0 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -1,11 +1,11 @@ /* - * Copyright (C) 2014 Martin Willi - * Copyright (C) 2014 revosec AG - * * Copyright (C) 2016 Tobias Brunner * Copyright (C) 2015 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * * 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 * Free Software Foundation; either version 2 of the License, or (at your @@ -30,15 +30,33 @@ #include #include #include +#include #include +#define HASH_SIZE_SHA1_HEX (2 * HASH_SIZE_SHA1) + +/** + * Context used to track loaded secrets + */ +typedef struct { + /** vici connection */ + vici_conn_t *conn; + /** format options */ + command_format_options_t format; + /** read setting */ + settings_t *cfg; + /** don't prompt user for password */ + bool noprompt; + /** list of key ids of loaded private keys */ + hashtable_t *keys; +} load_ctx_t; + /** * Load a single certificate over vici */ -static bool load_cert(vici_conn_t *conn, command_format_options_t format, - char *dir, certificate_type_t type, x509_flag_t flag, - chunk_t data) +static bool load_cert(load_ctx_t *ctx, char *dir, certificate_type_t type, + x509_flag_t flag, chunk_t data) { vici_req_t *req; vici_res_t *res; @@ -53,15 +71,15 @@ static bool load_cert(vici_conn_t *conn, command_format_options_t format, } vici_add_key_value(req, "data", data.ptr, data.len); - res = vici_submit(req, conn); + res = vici_submit(req, ctx->conn); if (!res) { fprintf(stderr, "load-cert request failed: %s\n", strerror(errno)); return FALSE; } - if (format & COMMAND_FORMAT_RAW) + if (ctx->format & COMMAND_FORMAT_RAW) { - vici_dump(res, "load-cert reply", format & COMMAND_FORMAT_PRETTY, + vici_dump(res, "load-cert reply", ctx->format & COMMAND_FORMAT_PRETTY, stdout); } else if (!streq(vici_find_str(res, "no", "success"), "yes")) @@ -81,8 +99,7 @@ static bool load_cert(vici_conn_t *conn, command_format_options_t format, /** * Load certficiates from a directory */ -static void load_certs(vici_conn_t *conn, command_format_options_t format, - char *type_str, char *dir) +static void load_certs(load_ctx_t *ctx, char *type_str, char *dir) { enumerator_t *enumerator; certificate_type_t type; @@ -103,7 +120,7 @@ static void load_certs(vici_conn_t *conn, command_format_options_t format, map = chunk_map(path, FALSE); if (map) { - load_cert(conn, format, path, type, flag, *map); + load_cert(ctx, path, type, flag, *map); chunk_unmap(map); } else @@ -120,8 +137,7 @@ static void load_certs(vici_conn_t *conn, command_format_options_t format, /** * Load a single private key over vici */ -static bool load_key(vici_conn_t *conn, command_format_options_t format, - char *dir, char *type, chunk_t data) +static bool load_key(load_ctx_t *ctx, char *dir, char *type, chunk_t data) { vici_req_t *req; vici_res_t *res; @@ -140,15 +156,15 @@ static bool load_key(vici_conn_t *conn, command_format_options_t format, } vici_add_key_value(req, "data", data.ptr, data.len); - res = vici_submit(req, conn); + res = vici_submit(req, ctx->conn); if (!res) { fprintf(stderr, "load-key request failed: %s\n", strerror(errno)); return FALSE; } - if (format & COMMAND_FORMAT_RAW) + if (ctx->format & COMMAND_FORMAT_RAW) { - vici_dump(res, "load-key reply", format & COMMAND_FORMAT_PRETTY, + vici_dump(res, "load-key reply", ctx->format & COMMAND_FORMAT_PRETTY, stdout); } else if (!streq(vici_find_str(res, "no", "success"), "yes")) @@ -168,11 +184,12 @@ static bool load_key(vici_conn_t *conn, command_format_options_t format, /** * Load a private key of any type to vici */ -static bool load_key_anytype(vici_conn_t *conn, command_format_options_t format, - char *path, private_key_t *private) +static bool load_key_anytype(load_ctx_t *ctx, char *path, + private_key_t *private) { bool loaded = FALSE; - chunk_t encoding; + chunk_t encoding, keyid; + char hex[HASH_SIZE_SHA1_HEX + 1]; if (!private->get_encoding(private, PRIVKEY_ASN1_DER, &encoding)) { @@ -182,18 +199,25 @@ static bool load_key_anytype(vici_conn_t *conn, command_format_options_t format, switch (private->get_type(private)) { case KEY_RSA: - loaded = load_key(conn, format, path, "rsa", encoding); + loaded = load_key(ctx, path, "rsa", encoding); break; case KEY_ECDSA: - loaded = load_key(conn, format, path, "ecdsa", encoding); + loaded = load_key(ctx, path, "ecdsa", encoding); break; case KEY_BLISS: - loaded = load_key(conn, format, path, "bliss", encoding); + loaded = load_key(ctx, path, "bliss", encoding); break; default: fprintf(stderr, "unsupported key type in '%s'\n", path); break; } + + if (loaded && + private->get_fingerprint(private, KEYID_PUBKEY_SHA1, &keyid) && + snprintf(hex, sizeof(hex), "%+B", &keyid) == HASH_SIZE_SHA1_HEX) + { + free(ctx->keys->remove(ctx->keys, hex)); + } chunk_clear(&encoding); return loaded; } @@ -312,7 +336,7 @@ static void* decrypt(char *name, char *type, chunk_t encoding) /** * Try to parse a potentially encrypted credential using configured secret */ -static void* decrypt_with_config(settings_t *cfg, char *name, char *type, +static void* decrypt_with_config(load_ctx_t *ctx, char *name, char *type, chunk_t encoding) { credential_type_t credtype; @@ -329,16 +353,16 @@ static void* decrypt_with_config(settings_t *cfg, char *name, char *type, } /* load all secrets for this key type */ - enumerator = cfg->create_section_enumerator(cfg, "secrets"); + enumerator = ctx->cfg->create_section_enumerator(ctx->cfg, "secrets"); while (enumerator->enumerate(enumerator, §ion)) { if (strpfx(section, type)) { - file = cfg->get_str(cfg, "secrets.%s.file", NULL, section); + file = ctx->cfg->get_str(ctx->cfg, "secrets.%s.file", NULL, section); if (file && strcaseeq(file, name)) { snprintf(buf, sizeof(buf), "secrets.%s", section); - secrets = cfg->create_key_value_enumerator(cfg, buf); + secrets = ctx->cfg->create_key_value_enumerator(ctx->cfg, buf); while (secrets->enumerate(secrets, &key, &value)) { if (strpfx(key, "secret")) @@ -382,22 +406,20 @@ static void* decrypt_with_config(settings_t *cfg, char *name, char *type, /** * Try to decrypt and load a private key */ -static bool load_encrypted_key(vici_conn_t *conn, - command_format_options_t format, settings_t *cfg, - char *rel, char *path, char *type, bool noprompt, - chunk_t data) +static bool load_encrypted_key(load_ctx_t *ctx, char *rel, char *path, + char *type, chunk_t data) { private_key_t *private; bool loaded = FALSE; - private = decrypt_with_config(cfg, rel, type, data); - if (!private && !noprompt) + private = decrypt_with_config(ctx, rel, type, data); + if (!private && !ctx->noprompt) { private = decrypt(rel, type, data); } if (private) { - loaded = load_key_anytype(conn, format, path, private); + loaded = load_key_anytype(ctx, path, private); private->destroy(private); } return loaded; @@ -406,8 +428,7 @@ static bool load_encrypted_key(vici_conn_t *conn, /** * Load private keys from a directory */ -static void load_keys(vici_conn_t *conn, command_format_options_t format, - bool noprompt, settings_t *cfg, char *type, char *dir) +static void load_keys(load_ctx_t *ctx, char *type, char *dir) { enumerator_t *enumerator; struct stat st; @@ -424,10 +445,9 @@ static void load_keys(vici_conn_t *conn, command_format_options_t format, map = chunk_map(path, FALSE); if (map) { - if (!load_encrypted_key(conn, format, cfg, rel, path, type, - noprompt, *map)) + if (!load_encrypted_key(ctx, rel, path, type, *map)) { - load_key(conn, format, path, type, *map); + load_key(ctx, path, type, *map); } chunk_unmap(map); } @@ -445,8 +465,7 @@ static void load_keys(vici_conn_t *conn, command_format_options_t format, /** * Load credentials from a PKCS#12 container over vici */ -static bool load_pkcs12(vici_conn_t *conn, command_format_options_t format, - char *path, pkcs12_t *p12) +static bool load_pkcs12(load_ctx_t *ctx, char *path, pkcs12_t *p12) { enumerator_t *enumerator; certificate_t *cert; @@ -460,8 +479,7 @@ static bool load_pkcs12(vici_conn_t *conn, command_format_options_t format, loaded = FALSE; if (cert->get_encoding(cert, CERT_ASN1_DER, &encoding)) { - loaded = load_cert(conn, format, path, CERT_X509, X509_NONE, - encoding); + loaded = load_cert(ctx, path, CERT_X509, X509_NONE, encoding); if (loaded) { fprintf(stderr, " %Y\n", cert->get_subject(cert)); @@ -478,7 +496,7 @@ static bool load_pkcs12(vici_conn_t *conn, command_format_options_t format, enumerator = p12->create_key_enumerator(p12); while (loaded && enumerator->enumerate(enumerator, &private)) { - loaded = load_key_anytype(conn, format, path, private); + loaded = load_key_anytype(ctx, path, private); } enumerator->destroy(enumerator); @@ -488,15 +506,14 @@ static bool load_pkcs12(vici_conn_t *conn, command_format_options_t format, /** * Try to decrypt and load credentials from a container */ -static bool load_encrypted_container(vici_conn_t *conn, - command_format_options_t format, settings_t *cfg, char *rel, - char *path, char *type, bool noprompt, chunk_t data) +static bool load_encrypted_container(load_ctx_t *ctx, char *rel, char *path, + char *type, chunk_t data) { container_t *container; bool loaded = FALSE; - container = decrypt_with_config(cfg, rel, type, data); - if (!container && !noprompt) + container = decrypt_with_config(ctx, rel, type, data); + if (!container && !ctx->noprompt) { container = decrypt(rel, type, data); } @@ -505,7 +522,7 @@ static bool load_encrypted_container(vici_conn_t *conn, switch (container->get_type(container)) { case CONTAINER_PKCS12: - loaded = load_pkcs12(conn, format, path, (pkcs12_t*)container); + loaded = load_pkcs12(ctx, path, (pkcs12_t*)container); break; default: break; @@ -518,8 +535,7 @@ static bool load_encrypted_container(vici_conn_t *conn, /** * Load credential containers from a directory */ -static void load_containers(vici_conn_t *conn, command_format_options_t format, - bool noprompt, settings_t *cfg, char *type, char *dir) +static void load_containers(load_ctx_t *ctx, char *type, char *dir) { enumerator_t *enumerator; struct stat st; @@ -536,8 +552,7 @@ static void load_containers(vici_conn_t *conn, command_format_options_t format, map = chunk_map(path, FALSE); if (map) { - load_encrypted_container(conn, format, cfg, rel, path, - type, noprompt, *map); + load_encrypted_container(ctx, rel, path, type, *map); chunk_unmap(map); } else @@ -554,8 +569,7 @@ static void load_containers(vici_conn_t *conn, command_format_options_t format, /** * Load a single secret over VICI */ -static bool load_secret(vici_conn_t *conn, settings_t *cfg, - char *section, command_format_options_t format) +static bool load_secret(load_ctx_t *ctx, char *section) { enumerator_t *enumerator; vici_req_t *req; @@ -594,7 +608,7 @@ static bool load_secret(vici_conn_t *conn, settings_t *cfg, return TRUE; } - value = cfg->get_str(cfg, "secrets.%s.secret", NULL, section); + value = ctx->cfg->get_str(ctx->cfg, "secrets.%s.secret", NULL, section); if (!value) { fprintf(stderr, "missing secret in '%s', ignored\n", section); @@ -621,7 +635,7 @@ static bool load_secret(vici_conn_t *conn, settings_t *cfg, vici_begin_list(req, "owners"); snprintf(buf, sizeof(buf), "secrets.%s", section); - enumerator = cfg->create_key_value_enumerator(cfg, buf); + enumerator = ctx->cfg->create_key_value_enumerator(ctx->cfg, buf); while (enumerator->enumerate(enumerator, &key, &value)) { if (strpfx(key, "id")) @@ -632,15 +646,15 @@ static bool load_secret(vici_conn_t *conn, settings_t *cfg, enumerator->destroy(enumerator); vici_end_list(req); - res = vici_submit(req, conn); + res = vici_submit(req, ctx->conn); if (!res) { fprintf(stderr, "load-shared request failed: %s\n", strerror(errno)); return FALSE; } - if (format & COMMAND_FORMAT_RAW) + if (ctx->format & COMMAND_FORMAT_RAW) { - vici_dump(res, "load-shared reply", format & COMMAND_FORMAT_PRETTY, + vici_dump(res, "load-shared reply", ctx->format & COMMAND_FORMAT_PRETTY, stdout); } else if (!streq(vici_find_str(res, "no", "success"), "yes")) @@ -657,6 +671,75 @@ static bool load_secret(vici_conn_t *conn, settings_t *cfg, return ret; } +CALLBACK(get_id, int, + hashtable_t *ht, vici_res_t *res, char *name, void *value, int len) +{ + if (streq(name, "keys")) + { + char *str; + + if (asprintf(&str, "%.*s", len, value) != -1) + { + free(ht->put(ht, str, str)); + } + } + return 0; +} + +/** + * Get a list of currently loaded private keys + */ +static void get_keys(load_ctx_t *ctx) +{ + vici_res_t *res; + + res = vici_submit(vici_begin("get-keys"), ctx->conn); + if (res) + { + if (ctx->format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "get-keys reply", ctx->format & COMMAND_FORMAT_PRETTY, + stdout); + } + vici_parse_cb(res, NULL, NULL, get_id, ctx->keys); + vici_free_res(res); + } +} + +/** + * Remove a given key + */ +static bool unload_key(load_ctx_t *ctx, char *id) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + + req = vici_begin("unload-key"); + + vici_add_key_valuef(req, "id", "%s", id); + + res = vici_submit(req, ctx->conn); + if (!res) + { + fprintf(stderr, "unload-key request failed: %s\n", strerror(errno)); + return FALSE; + } + if (ctx->format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "unload-key reply", ctx->format & COMMAND_FORMAT_PRETTY, + stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "unloading key '%s' failed: %s\n", + id, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + /** * Clear all currently loaded credentials */ @@ -686,7 +769,14 @@ int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, settings_t *cfg, bool clear, bool noprompt) { enumerator_t *enumerator; - char *section; + char *section, *id; + load_ctx_t ctx = { + .conn = conn, + .format = format, + .noprompt = noprompt, + .cfg = cfg, + .keys = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8), + }; if (clear) { @@ -696,29 +786,38 @@ int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, } } - load_certs(conn, format, "x509", SWANCTL_X509DIR); - load_certs(conn, format, "x509ca", SWANCTL_X509CADIR); - load_certs(conn, format, "x509ocsp", SWANCTL_X509OCSPDIR); - load_certs(conn, format, "x509aa", SWANCTL_X509AADIR); - load_certs(conn, format, "x509ac", SWANCTL_X509ACDIR); - load_certs(conn, format, "x509crl", SWANCTL_X509CRLDIR); - load_certs(conn, format, "pubkey", SWANCTL_PUBKEYDIR); + get_keys(&ctx); - load_keys(conn, format, noprompt, cfg, "private", SWANCTL_PRIVATEDIR); - load_keys(conn, format, noprompt, cfg, "rsa", SWANCTL_RSADIR); - load_keys(conn, format, noprompt, cfg, "ecdsa", SWANCTL_ECDSADIR); - load_keys(conn, format, noprompt, cfg, "bliss", SWANCTL_BLISSDIR); - load_keys(conn, format, noprompt, cfg, "pkcs8", SWANCTL_PKCS8DIR); + load_certs(&ctx, "x509", SWANCTL_X509DIR); + load_certs(&ctx, "x509ca", SWANCTL_X509CADIR); + load_certs(&ctx, "x509ocsp", SWANCTL_X509OCSPDIR); + load_certs(&ctx, "x509aa", SWANCTL_X509AADIR); + load_certs(&ctx, "x509ac", SWANCTL_X509ACDIR); + load_certs(&ctx, "x509crl", SWANCTL_X509CRLDIR); + load_certs(&ctx, "pubkey", SWANCTL_PUBKEYDIR); - load_containers(conn, format, noprompt, cfg, "pkcs12", SWANCTL_PKCS12DIR); + load_keys(&ctx, "private", SWANCTL_PRIVATEDIR); + load_keys(&ctx, "rsa", SWANCTL_RSADIR); + load_keys(&ctx, "ecdsa", SWANCTL_ECDSADIR); + load_keys(&ctx, "bliss", SWANCTL_BLISSDIR); + load_keys(&ctx, "pkcs8", SWANCTL_PKCS8DIR); + + load_containers(&ctx, "pkcs12", SWANCTL_PKCS12DIR); enumerator = cfg->create_section_enumerator(cfg, "secrets"); while (enumerator->enumerate(enumerator, §ion)) { - load_secret(conn, cfg, section, format); + load_secret(&ctx, section); } enumerator->destroy(enumerator); + enumerator = ctx.keys->create_enumerator(ctx.keys); + while (enumerator->enumerate(enumerator, &id, NULL)) + { + unload_key(&ctx, id); + } + enumerator->destroy(enumerator); + ctx.keys->destroy_function(ctx.keys, (void*)free); return 0; } From bafd851896bc64a452673a2b97ac978af5617871 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 16:20:03 +0100 Subject: [PATCH 07/33] mem-cred: Add methods to add/remove shared keys with unique identifiers Also added is a method to enumerate the unique identifiers. --- src/libstrongswan/credentials/sets/mem_cred.c | 86 +++++++++++++++++-- src/libstrongswan/credentials/sets/mem_cred.h | 27 ++++++ 2 files changed, 107 insertions(+), 6 deletions(-) diff --git a/src/libstrongswan/credentials/sets/mem_cred.c b/src/libstrongswan/credentials/sets/mem_cred.c index 110986f1a..53e035f98 100644 --- a/src/libstrongswan/credentials/sets/mem_cred.c +++ b/src/libstrongswan/credentials/sets/mem_cred.c @@ -400,10 +400,12 @@ METHOD(mem_cred_t, remove_key, bool, * Shared key entry */ typedef struct { - /* shared key */ + /** shared key */ shared_key_t *shared; - /* list of owners, identification_t */ + /** list of owners, identification_t */ linked_list_t *owners; + /** optional unique identifier */ + char *id; } shared_entry_t; /** @@ -414,11 +416,12 @@ static void shared_entry_destroy(shared_entry_t *entry) entry->owners->destroy_offset(entry->owners, offsetof(identification_t, destroy)); entry->shared->destroy(entry->shared); + free(entry->id); free(entry); } /** - * Check if two shared key entries equal + * Check if two shared key entries are equal (ignoring the unique identifier) */ static bool shared_entry_equals(shared_entry_t *a, shared_entry_t *b) { @@ -554,8 +557,9 @@ METHOD(credential_set_t, create_shared_enumerator, enumerator_t*, (void*)shared_filter, data, (void*)shared_data_destroy); } -METHOD(mem_cred_t, add_shared_list, void, - private_mem_cred_t *this, shared_key_t *shared, linked_list_t* owners) +METHOD(mem_cred_t, add_shared_unique, void, + private_mem_cred_t *this, char *id, shared_key_t *shared, + linked_list_t* owners) { shared_entry_t *current, *new; enumerator_t *enumerator; @@ -563,6 +567,7 @@ METHOD(mem_cred_t, add_shared_list, void, INIT(new, .shared = shared, .owners = owners, + .id = strdupnull(id), ); this->lock->write_lock(this->lock); @@ -570,7 +575,10 @@ METHOD(mem_cred_t, add_shared_list, void, enumerator = this->shared->create_enumerator(this->shared); while (enumerator->enumerate(enumerator, ¤t)) { - if (shared_entry_equals(current, new)) + /* always replace keys with the same unique identifier, only compare + * them if both have no unique id assigned */ + if ((id && streq(id, current->id)) || + (!id && !current->id && shared_entry_equals(current, new))) { this->shared->remove_at(this->shared, enumerator); shared_entry_destroy(current); @@ -584,6 +592,12 @@ METHOD(mem_cred_t, add_shared_list, void, this->lock->unlock(this->lock); } +METHOD(mem_cred_t, add_shared_list, void, + private_mem_cred_t *this, shared_key_t *shared, linked_list_t* owners) +{ + add_shared_unique(this, NULL, shared, owners); +} + METHOD(mem_cred_t, add_shared, void, private_mem_cred_t *this, shared_key_t *shared, ...) { @@ -606,6 +620,63 @@ METHOD(mem_cred_t, add_shared, void, add_shared_list(this, shared, owners); } +METHOD(mem_cred_t, remove_shared_unique, void, + private_mem_cred_t *this, char *id) +{ + enumerator_t *enumerator; + shared_entry_t *current; + + if (!id) + { + return; + } + + this->lock->write_lock(this->lock); + + enumerator = this->shared->create_enumerator(this->shared); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(id, current->id)) + { + this->shared->remove_at(this->shared, enumerator); + shared_entry_destroy(current); + break; + } + } + enumerator->destroy(enumerator); + + this->lock->unlock(this->lock); +} + +/** + * Filter unique ids of shared keys (ingore secrets without unique id) + */ +static bool unique_filter(void *unused, + shared_entry_t **in, char **id) +{ + shared_entry_t *entry = *in; + + if (!entry->id) + { + return FALSE; + } + if (id) + { + *id = entry->id; + } + return TRUE; +} + +METHOD(mem_cred_t, create_unique_shared_enumerator, enumerator_t*, + private_mem_cred_t *this) +{ + this->lock->read_lock(this->lock); + return enumerator_create_filter( + this->shared->create_enumerator(this->shared), + (void*)unique_filter, this->lock, + (void*)this->lock->unlock); +} + /** * Certificate distribution point */ @@ -846,6 +917,9 @@ mem_cred_t *mem_cred_create() .remove_key = _remove_key, .add_shared = _add_shared, .add_shared_list = _add_shared_list, + .add_shared_unique = _add_shared_unique, + .remove_shared_unique = _remove_shared_unique, + .create_unique_shared_enumerator = _create_unique_shared_enumerator, .add_cdp = _add_cdp, .replace_certs = _replace_certs, .replace_secrets = _replace_secrets, diff --git a/src/libstrongswan/credentials/sets/mem_cred.h b/src/libstrongswan/credentials/sets/mem_cred.h index ac125d4e8..135515260 100644 --- a/src/libstrongswan/credentials/sets/mem_cred.h +++ b/src/libstrongswan/credentials/sets/mem_cred.h @@ -112,6 +112,33 @@ struct mem_cred_t { void (*add_shared_list)(mem_cred_t *this, shared_key_t *shared, linked_list_t *owners); + /** + * Add a shared key to the credential set, associated with the given unique + * identifier. + * + * If a shared key with the same id already exists it is replaced. + * + * @param id unique identifier of this key (cloned) + * @param shared shared key to add, gets owned by set + * @param ... NULL terminated list of owners (identification_t*) + */ + void (*add_shared_unique)(mem_cred_t *this, char *id, shared_key_t *shared, + linked_list_t *owners); + + /** + * Remove a shared key by its unique identifier. + * + * @param id unique identifier of this key + */ + void (*remove_shared_unique)(mem_cred_t *this, char *id); + + /** + * Create an enumerator over the unique identifiers of shared keys. + * + * @return enumerator over char* + */ + enumerator_t *(*create_unique_shared_enumerator)(mem_cred_t *this); + /** * Add a certificate distribution point to the set. * From cf57d9a98f1ecf757f8f6934e3d0303e2d2aa486 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 16:27:01 +0100 Subject: [PATCH 08/33] vici: Add possibility to remove shared keys by a unique identifier This identifier can be set when adding/replacing a secret. The unique identifiers of all secrets may be enumerated. --- src/libcharon/plugins/vici/README.md | 26 +++++++++++- src/libcharon/plugins/vici/vici_cred.c | 55 ++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index a46c35a28..edcc7eae5 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -493,7 +493,8 @@ including keys found in other backends. Load a shared IKE PSK, EAP or XAuth secret into the daemon. { - type = + id = + type = data = owners = [ @@ -503,6 +504,29 @@ Load a shared IKE PSK, EAP or XAuth secret into the daemon. errmsg = } +### unload-shared() ### + +Unload a previously loaded shared IKE PSK, EAP or XAuth secret by its unique +identifier. + + { + id = + } => { + success = + errmsg = + } + +### get-shared() ### + +Return a list of unique identifiers of shared keys loaded exclusively over vici, +not including keys found in other backends. + + {} => { + keys = [ + + ] + } + ### flush-certs() ### Flushes the certificate cache. The optional type argument allows to flush diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index 03649acfe..04a13b4fa 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -320,11 +320,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) { @@ -371,15 +372,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) { @@ -426,6 +471,8 @@ static void manage_commands(private_vici_cred_t *this, bool reg) manage_command(this, "unload-key", unload_key, reg); manage_command(this, "get-keys", get_keys, 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*, From d460ab2bff5c49cc2e5fbffe79f59d9a1d9918a6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 9 Nov 2016 16:49:35 +0100 Subject: [PATCH 09/33] swanctl: Automatically unload removed shared keys --- src/swanctl/commands/load_creds.c | 64 +++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 6c084e5e0..126b61e64 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -50,6 +50,8 @@ typedef struct { bool noprompt; /** list of key ids of loaded private keys */ hashtable_t *keys; + /** list of unique ids of loaded shared keys */ + hashtable_t *shared; } load_ctx_t; /** @@ -629,6 +631,7 @@ static bool load_secret(load_ctx_t *ctx, char *section) req = vici_begin("load-shared"); + vici_add_key_valuef(req, "id", "%s", section); vici_add_key_valuef(req, "type", "%s", type); vici_add_key_value(req, "data", data.ptr, data.len); chunk_clear(&data); @@ -667,6 +670,10 @@ static bool load_secret(load_ctx_t *ctx, char *section) { printf("loaded %s secret '%s'\n", type, section); } + if (ret) + { + free(ctx->shared->remove(ctx->shared, section)); + } vici_free_res(res); return ret; } @@ -687,9 +694,9 @@ CALLBACK(get_id, int, } /** - * Get a list of currently loaded private keys + * Get a list of currently loaded private and shared keys */ -static void get_keys(load_ctx_t *ctx) +static void get_creds(load_ctx_t *ctx) { vici_res_t *res; @@ -704,31 +711,43 @@ static void get_keys(load_ctx_t *ctx) vici_parse_cb(res, NULL, NULL, get_id, ctx->keys); vici_free_res(res); } + res = vici_submit(vici_begin("get-shared"), ctx->conn); + if (res) + { + if (ctx->format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "get-shared reply", ctx->format & COMMAND_FORMAT_PRETTY, + stdout); + } + vici_parse_cb(res, NULL, NULL, get_id, ctx->shared); + vici_free_res(res); + } } /** * Remove a given key */ -static bool unload_key(load_ctx_t *ctx, char *id) +static bool unload_key(load_ctx_t *ctx, char *command, char *id) { vici_req_t *req; vici_res_t *res; + char buf[BUF_LEN]; bool ret = TRUE; - req = vici_begin("unload-key"); + req = vici_begin(command); vici_add_key_valuef(req, "id", "%s", id); res = vici_submit(req, ctx->conn); if (!res) { - fprintf(stderr, "unload-key request failed: %s\n", strerror(errno)); + fprintf(stderr, "%s request failed: %s\n", command, strerror(errno)); return FALSE; } if (ctx->format & COMMAND_FORMAT_RAW) { - vici_dump(res, "unload-key reply", ctx->format & COMMAND_FORMAT_PRETTY, - stdout); + snprintf(buf, sizeof(buf), "%s reply", command); + vici_dump(res, buf, ctx->format & COMMAND_FORMAT_PRETTY, stdout); } else if (!streq(vici_find_str(res, "no", "success"), "yes")) { @@ -740,6 +759,22 @@ static bool unload_key(load_ctx_t *ctx, char *id) return ret; } +/** + * Remove all keys in the given hashtable using the given command + */ +static void unload_keys(load_ctx_t *ctx, hashtable_t *ht, char *command) +{ + enumerator_t *enumerator; + char *id; + + enumerator = ht->create_enumerator(ht); + while (enumerator->enumerate(enumerator, &id, NULL)) + { + unload_key(ctx, command, id); + } + enumerator->destroy(enumerator); +} + /** * Clear all currently loaded credentials */ @@ -769,13 +804,14 @@ int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, settings_t *cfg, bool clear, bool noprompt) { enumerator_t *enumerator; - char *section, *id; + char *section; load_ctx_t ctx = { .conn = conn, .format = format, .noprompt = noprompt, .cfg = cfg, .keys = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8), + .shared = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8), }; if (clear) @@ -786,7 +822,7 @@ int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, } } - get_keys(&ctx); + get_creds(&ctx); load_certs(&ctx, "x509", SWANCTL_X509DIR); load_certs(&ctx, "x509ca", SWANCTL_X509CADIR); @@ -811,13 +847,11 @@ int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, } enumerator->destroy(enumerator); - enumerator = ctx.keys->create_enumerator(ctx.keys); - while (enumerator->enumerate(enumerator, &id, NULL)) - { - unload_key(&ctx, id); - } - enumerator->destroy(enumerator); + unload_keys(&ctx, ctx.keys, "unload-key"); + unload_keys(&ctx, ctx.shared, "unload-shared"); + ctx.keys->destroy_function(ctx.keys, (void*)free); + ctx.shared->destroy_function(ctx.shared, (void*)free); return 0; } From 44fcc83310ed7a032d2cf0fa315f3f7bd36c0f69 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 11 Nov 2016 10:40:53 +0100 Subject: [PATCH 10/33] vici: Add missing dscp setting for IKE_SAs Fixes #2170. --- src/libcharon/plugins/vici/vici_config.c | 44 +++++++++++++++++++++--- src/swanctl/swanctl.opt | 8 +++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 2110fd31d..ff706be46 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -295,6 +295,7 @@ typedef struct { uint64_t rekey_time; uint64_t over_time; uint64_t rand_time; + uint8_t dscp; } peer_data_t; /** @@ -370,6 +371,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); @@ -814,10 +816,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 +827,7 @@ CALLBACK(parse_uint32, bool, { return FALSE; } - l = strtoul(buf, &end, 0); + l = strtoul(buf, &end, base); if (*end == 0) { *out = l; @@ -835,6 +836,24 @@ CALLBACK(parse_uint32, bool, return FALSE; } +/** + * 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 */ @@ -983,6 +1002,20 @@ CALLBACK(parse_tfc, bool, return parse_uint32(out, v); } +/** + * 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 */ @@ -1417,6 +1450,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 }, @@ -2085,7 +2119,7 @@ CALLBACK(config_sn, bool, 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, diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index a7d6d9fc3..e882e60ba 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -102,6 +102,14 @@ connections..pull = yes Push mode is currently supported for IKEv1, but not in IKEv2. It is used by a few implementations only, pull mode is recommended. +connections..dscp = 000000 + Differentiated Services Field Codepoint to set on outgoing IKE packets (six + binary digits). + + Differentiated Services Field Codepoint to set on outgoing IKE packets for + this connection. The value is a six digit binary encoded string specifying + the Codepoint to set, as defined in RFC 2474. + connections..encap = no Enforce UDP encapsulation by faking NAT-D payloads. From e00bc9f6b298bf2ae8151bee83c346d6f867c0cb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 16 Nov 2016 15:37:23 +0100 Subject: [PATCH 11/33] vici: Add support for certificate policies --- src/libcharon/plugins/vici/vici_config.c | 17 +++++++++++++++++ src/swanctl/commands/load_conns.c | 1 + src/swanctl/swanctl.opt | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index ff706be46..add81b9ef 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -1141,6 +1141,22 @@ CALLBACK(parse_group, bool, return parse_id(cfg, AUTH_RULE_GROUP, v); } +/** + * Parse certificate policy + */ +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; +} + /** * Parse a certificate; add as auth rule to config */ @@ -1402,6 +1418,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 }, diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 2e443a94a..82592f456 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -38,6 +38,7 @@ static bool is_list_key(char *key) "vips", "pools", "groups", + "cert_policy", }; int i; diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index e882e60ba..e74886641 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -406,6 +406,12 @@ connections..remote.groups = can be certified by different means, for example by appropriate Attribute Certificates or by an AAA backend involved in the authentication. +connections..remote.cert_policy = + Certificate policy OIDs the peer's certificate must have. + + Comma separated list of certificate policy OIDs the peer's certificate must + have. OIDs are specified using the numerical dotted representation. + connections..remote.certs = Comma separated list of certificate to accept for authentication. From 3bedf10b25fe8d5241709a446b3e1faffdc79b01 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 16 Nov 2016 15:58:34 +0100 Subject: [PATCH 12/33] vici: Add support for IPv6 Transport Proxy Mode --- src/libcharon/plugins/vici/vici_config.c | 21 +++++++++------- src/libcharon/plugins/vici/vici_query.c | 32 ++++++++++++++++++++---- src/swanctl/swanctl.opt | 8 +++--- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index add81b9ef..b16e23a5a 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -463,7 +463,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) @@ -772,20 +773,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; @@ -1383,7 +1386,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 }, diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index e3a16f5ea..3f7d71e79 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -92,6 +92,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 */ @@ -108,7 +131,7 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, 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) @@ -455,7 +478,7 @@ static void raise_policy(private_vici_query_t *this, u_int id, child_sa_t *child b = vici_builder_create(); b->begin_section(b, child->get_name(child)); - 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); @@ -495,7 +518,7 @@ static void raise_policy_cfg(private_vici_query_t *this, u_int id, b = vici_builder_create(); b->begin_section(b, cfg->get_name(cfg)); - 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); @@ -757,8 +780,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); diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index e74886641..327b8971d 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -654,11 +654,13 @@ connections..children..hostaccess = yes Hostaccess variable to pass to **updown** script. connections..children..mode = tunnel - IPsec Mode to establish (_tunnel_, _transport_, _beet_, _pass_ or _drop_). + IPsec Mode to establish (_tunnel_, _transport_, _transport_proxy_, _beet_, + _pass_ or _drop_). IPsec Mode to establish CHILD_SA with. _tunnel_ negotiates the CHILD_SA - in IPsec Tunnel Mode, whereas _transport_ uses IPsec Transport Mode. _beet_ - is the Bound End to End Tunnel mixture mode, working with fixed inner + in IPsec Tunnel Mode, whereas _transport_ uses IPsec Transport Mode. + _transport_proxy_ signifying the special Mobile IPv6 Transport Proxy Mode. + _beet_ is the Bound End to End Tunnel mixture mode, working with fixed inner addresses without the need to include them in each packet. Both _transport_ and _beet_ modes are subject to mode negotiation; _tunnel_ From ed105f45afca41b4e445c18f24f219352a4c6ef0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 16 Nov 2016 17:12:33 +0100 Subject: [PATCH 13/33] vici: Add support for NT Hash secrets Fixes #1002. --- src/libcharon/plugins/vici/vici_cred.c | 4 ++++ src/swanctl/commands/load_creds.c | 4 +++- src/swanctl/swanctl.opt | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index 04a13b4fa..c8d71138c 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -339,6 +339,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); diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 126b61e64..836017893 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -583,6 +583,7 @@ static bool load_secret(load_ctx_t *ctx, char *section) char *types[] = { "eap", "xauth", + "ntlm", "ike", "private", "rsa", @@ -605,7 +606,8 @@ static bool load_secret(load_ctx_t *ctx, char *section) fprintf(stderr, "ignoring unsupported secret '%s'\n", section); return FALSE; } - if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ike")) + if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ntlm") && + !streq(type, "ike")) { /* skip non-shared secrets */ return TRUE; } diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 327b8971d..8ddc9f750 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -831,6 +831,28 @@ secrets.eap.id = be specified, each having an _id_ prefix, if a secret is shared between multiple users. +secrets.ntlm { # } + NTLM secret section for a specific secret. + + NTLM secret section for a specific secret. Each NTLM secret is defined in + a unique section having the _ntlm_ prefix. NTLM secrets may only be used for + EAP-MSCHAPv2 authentication. + +secrets.ntlm.secret = + Value of the NTLM secret. + + Value of the NTLM secret, which is the NT Hash of the actual secret, that + is, MD4(UTF-16LE(secret)). The resulting 16-byte value may either be given + as a hex encoded string with a _0x_ prefix or as a Base64 encoded string + with a _0s_ prefix. + +secrets.ntlm.id = + Identity the NTLM secret belongs to. + + Identity the NTLM secret belongs to. Multiple unique identities may + be specified, each having an _id_ prefix, if a secret is shared between + multiple users. + secrets.ike { # } IKE preshared secret section for a specific secret. From 7a0fdbab42669d75cec2c073062331b7a7aed443 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 16 Nov 2016 17:59:22 +0100 Subject: [PATCH 14/33] shunt-manager: Add an optional namespace for each shunt This will allow us to reuse the names of child configs e.g. when they are defined in different connections. --- .../plugins/bypass_lan/bypass_lan_listener.c | 4 +- src/libcharon/plugins/stroke/stroke_control.c | 4 +- src/libcharon/plugins/stroke/stroke_list.c | 2 +- src/libcharon/plugins/unity/unity_handler.c | 5 +- src/libcharon/plugins/vici/vici_config.c | 4 +- src/libcharon/plugins/vici/vici_control.c | 4 +- src/libcharon/plugins/vici/vici_query.c | 2 +- .../processing/jobs/start_action_job.c | 3 +- src/libcharon/sa/shunt_manager.c | 90 ++++++++++++++----- src/libcharon/sa/shunt_manager.h | 13 +-- 10 files changed, 90 insertions(+), 41 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/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index fb60d3973..7b0602cfb 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -641,7 +641,7 @@ 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, NULL, child_cfg)) { fprintf(out, "'%s' shunt %N policy installed\n", name, ipsec_mode_names, mode); @@ -733,7 +733,7 @@ METHOD(stroke_control_t, unroute, void, enumerator_t *enumerator; uint32_t id = 0; - if (charon->shunts->uninstall(charon->shunts, msg->unroute.name)) + if (charon->shunts->uninstall(charon->shunts, NULL, msg->unroute.name)) { fprintf(out, "shunt policy '%s' uninstalled\n", msg->unroute.name); return; 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/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index b16e23a5a..dbbeb9e55 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -1757,7 +1757,7 @@ 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, NULL, child_cfg); break; default: charon->traps->install(charon->traps, peer_cfg, child_cfg, @@ -1865,7 +1865,7 @@ 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, NULL, name); break; default: enumerator = charon->traps->create_enumerator(charon->traps); diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 59c214437..20d19252b 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -565,7 +565,7 @@ 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, NULL, child_cfg); break; default: ok = charon->traps->install(charon->traps, peer_cfg, child_cfg, @@ -594,7 +594,7 @@ CALLBACK(uninstall, vici_message_t*, DBG1(DBG_CFG, "vici uninstall '%s'", child); - if (charon->shunts->uninstall(charon->shunts, child)) + if (charon->shunts->uninstall(charon->shunts, NULL, child)) { return send_reply(this, NULL); } diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index 3f7d71e79..692cd7f5d 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -580,7 +580,7 @@ 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, NULL, &child_cfg)) { if (child && !streq(child, child_cfg->get_name(child_cfg))) { diff --git a/src/libcharon/processing/jobs/start_action_job.c b/src/libcharon/processing/jobs/start_action_job.c index 5e88ac230..19f205251 100644 --- a/src/libcharon/processing/jobs/start_action_job.c +++ b/src/libcharon/processing/jobs/start_action_job.c @@ -68,7 +68,8 @@ METHOD(job_t, execute, job_requeue_t, mode = child_cfg->get_mode(child_cfg); if (mode == MODE_PASS || mode == MODE_DROP) { - charon->shunts->install(charon->shunts, child_cfg); + charon->shunts->install(charon->shunts, NULL, + child_cfg); } else { diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 40e291be5..b0162751d 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -36,7 +36,7 @@ struct private_shunt_manager_t { shunt_manager_t public; /** - * Installed shunts, as child_cfg_t + * Installed shunts, as entry_t */ linked_list_t *shunts; @@ -56,6 +56,32 @@ struct private_shunt_manager_t { rwlock_condvar_t *condvar; }; +/** + * Config entry for a shunt + */ +typedef struct { + /** + * Configured namespace + */ + char *ns; + + /** + * Child config + */ + child_cfg_t *cfg; + +} entry_t; + +/** + * Destroy a config entry + */ +static void entry_destroy(entry_t *this) +{ + this->cfg->destroy(this->cfg); + free(this->ns); + free(this); +} + /** * Install in and out shunt policies in the kernel */ @@ -162,10 +188,10 @@ static bool install_shunt_policy(child_cfg_t *child) } METHOD(shunt_manager_t, install, bool, - private_shunt_manager_t *this, child_cfg_t *child) + private_shunt_manager_t *this, char *ns, child_cfg_t *cfg) { enumerator_t *enumerator; - child_cfg_t *child_cfg; + entry_t *entry; bool found = FALSE, success; /* check if not already installed */ @@ -176,9 +202,10 @@ METHOD(shunt_manager_t, install, bool, return FALSE; } enumerator = this->shunts->create_enumerator(this->shunts); - while (enumerator->enumerate(enumerator, &child_cfg)) + while (enumerator->enumerate(enumerator, &entry)) { - if (streq(child_cfg->get_name(child_cfg), child->get_name(child))) + if (streq(ns, entry->ns) && + streq(cfg->get_name(cfg), entry->cfg->get_name(entry->cfg))) { found = TRUE; break; @@ -188,21 +215,25 @@ METHOD(shunt_manager_t, install, bool, if (found) { DBG1(DBG_CFG, "shunt %N policy '%s' already installed", - ipsec_mode_names, child->get_mode(child), child->get_name(child)); + ipsec_mode_names, cfg->get_mode(cfg), cfg->get_name(cfg)); this->lock->unlock(this->lock); return TRUE; } - this->shunts->insert_last(this->shunts, child->get_ref(child)); + INIT(entry, + .ns = strdupnull(ns), + .cfg = cfg->get_ref(cfg), + ); + this->shunts->insert_last(this->shunts, entry); this->installing++; this->lock->unlock(this->lock); - success = install_shunt_policy(child); + success = install_shunt_policy(cfg); this->lock->write_lock(this->lock); if (!success) { - this->shunts->remove(this->shunts, child, NULL); - child->destroy(child); + this->shunts->remove(this->shunts, entry, NULL); + entry_destroy(entry); } this->installing--; this->condvar->signal(this->condvar); @@ -320,19 +351,20 @@ static void uninstall_shunt_policy(child_cfg_t *child) } METHOD(shunt_manager_t, uninstall, bool, - private_shunt_manager_t *this, char *name) + private_shunt_manager_t *this, char *ns, char *name) { enumerator_t *enumerator; - child_cfg_t *child, *found = NULL; + entry_t *entry, *found = NULL; this->lock->write_lock(this->lock); enumerator = this->shunts->create_enumerator(this->shunts); - while (enumerator->enumerate(enumerator, &child)) + while (enumerator->enumerate(enumerator, &entry)) { - if (streq(name, child->get_name(child))) + if (streq(ns, entry->ns) && + streq(name, entry->cfg->get_name(entry->cfg))) { this->shunts->remove_at(this->shunts, enumerator); - found = child; + found = entry; break; } } @@ -343,8 +375,19 @@ METHOD(shunt_manager_t, uninstall, bool, { return FALSE; } - uninstall_shunt_policy(child); - child->destroy(child); + uninstall_shunt_policy(found->cfg); + entry_destroy(found); + return TRUE; +} + +CALLBACK(filter_entries, bool, + void *unused, entry_t **entry, char **ns, void **in, child_cfg_t **cfg) +{ + if (ns) + { + *ns = (*entry)->ns; + } + *cfg = (*entry)->cfg; return TRUE; } @@ -352,25 +395,26 @@ METHOD(shunt_manager_t, create_enumerator, enumerator_t*, private_shunt_manager_t *this) { this->lock->read_lock(this->lock); - return enumerator_create_cleaner( + return enumerator_create_filter( this->shunts->create_enumerator(this->shunts), - (void*)this->lock->unlock, this->lock); + (void*)filter_entries, this->lock, + (void*)this->lock->unlock); } METHOD(shunt_manager_t, flush, void, private_shunt_manager_t *this) { - child_cfg_t *child; + entry_t *entry; this->lock->write_lock(this->lock); while (this->installing) { this->condvar->wait(this->condvar, this->lock); } - while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS) + while (this->shunts->remove_last(this->shunts, (void**)&entry) == SUCCESS) { - uninstall_shunt_policy(child); - child->destroy(child); + uninstall_shunt_policy(entry->cfg); + entry_destroy(entry); } this->installing = INSTALL_DISABLED; this->lock->unlock(this->lock); diff --git a/src/libcharon/sa/shunt_manager.h b/src/libcharon/sa/shunt_manager.h index c43f5db3d..f2b721032 100644 --- a/src/libcharon/sa/shunt_manager.h +++ b/src/libcharon/sa/shunt_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Tobias Brunner + * Copyright (C) 2015-2016 Tobias Brunner * Copyright (C) 2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -36,23 +36,26 @@ struct shunt_manager_t { /** * Install a policy as a shunt. * - * @param child child configuration to install as a shunt + * @param ns optional namespace (e.g. name of a connection or + * plugin), cloned + * @param child child configuration to install as a shunt * @return TRUE if installed successfully */ - bool (*install)(shunt_manager_t *this, child_cfg_t *child); + bool (*install)(shunt_manager_t *this, char *ns, child_cfg_t *child); /** * Uninstall a shunt policy. * + * @param ns namespace (same as given during installation) * @param name name of child configuration to uninstall as a shunt * @return TRUE if uninstalled successfully */ - bool (*uninstall)(shunt_manager_t *this, char *name); + bool (*uninstall)(shunt_manager_t *this, char *ns, char *name); /** * Create an enumerator over all installed shunts. * - * @return enumerator over (child_sa_t) + * @return enumerator over (char*, child_cfg_t*) */ enumerator_t* (*create_enumerator)(shunt_manager_t *this); From 02767e430975eb4c832b6c8e5733a8c304e71515 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Feb 2017 16:13:32 +0100 Subject: [PATCH 15/33] stroke: Use peer name as namespace for shunt policies The same goes for the start-action-job. When unrouting, we search for the first policy with a matching child-cfg. --- src/libcharon/plugins/stroke/stroke_control.c | 20 +++++++++++++++++-- .../processing/jobs/start_action_job.c | 3 ++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index 7b0602cfb..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, NULL, 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, NULL, 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/processing/jobs/start_action_job.c b/src/libcharon/processing/jobs/start_action_job.c index 19f205251..654ec6abe 100644 --- a/src/libcharon/processing/jobs/start_action_job.c +++ b/src/libcharon/processing/jobs/start_action_job.c @@ -68,7 +68,8 @@ METHOD(job_t, execute, job_requeue_t, mode = child_cfg->get_mode(child_cfg); if (mode == MODE_PASS || mode == MODE_DROP) { - charon->shunts->install(charon->shunts, NULL, + charon->shunts->install(charon->shunts, + peer_cfg->get_name(peer_cfg), child_cfg); } else From 7627f5f9c7f8bf8edde1f98048e339fbe74615ba Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 16 Nov 2016 18:13:59 +0100 Subject: [PATCH 16/33] vici: Explicitly use peer name when uninstalling trap and shunt policies Also adds an `ike` parameter to the `uninstall` command. --- src/libcharon/plugins/vici/README.md | 4 ++- src/libcharon/plugins/vici/vici_config.c | 12 +++++--- src/libcharon/plugins/vici/vici_control.c | 34 +++++++++++++++++++---- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index edcc7eae5..b7b7f4ee0 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -312,7 +312,7 @@ Install a trap, drop or bypass policy defined by a CHILD_SA config. { child = - ike = + ike = } => { success = errmsg = @@ -324,6 +324,8 @@ Uninstall a trap, drop or bypass policy defined by a CHILD_SA config. { child = + ike = } => { success = errmsg = diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index dbbeb9e55..3b27bf7c3 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -1757,7 +1757,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, NULL, 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, @@ -1778,6 +1779,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; @@ -1865,13 +1867,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, NULL, 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; diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 20d19252b..05d0dc5a6 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -565,7 +565,8 @@ CALLBACK(install, vici_message_t*, { case MODE_PASS: case MODE_DROP: - ok = charon->shunts->install(charon->shunts, NULL, 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 +582,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 +598,35 @@ CALLBACK(uninstall, vici_message_t*, DBG1(DBG_CFG, "vici uninstall '%s'", child); - if (charon->shunts->uninstall(charon->shunts, NULL, 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; From ebb517581f3a4e0e0a47b2a52e0b7588bee83f06 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 11 Oct 2016 18:27:29 +0200 Subject: [PATCH 17/33] swanctl: Pass optional connection name to --initiate/install/uninstall --- src/swanctl/commands/initiate.c | 12 ++++++++++-- src/swanctl/commands/install.c | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/swanctl/commands/initiate.c b/src/swanctl/commands/initiate.c index eb7b6adbd..8e452a6f6 100644 --- a/src/swanctl/commands/initiate.c +++ b/src/swanctl/commands/initiate.c @@ -37,7 +37,7 @@ static int initiate(vici_conn_t *conn) vici_req_t *req; vici_res_t *res; command_format_options_t format = COMMAND_FORMAT_NONE; - char *arg, *child = NULL; + char *arg, *child = NULL, *ike = NULL; int ret = 0, timeout = 0, level = 1; while (TRUE) @@ -55,6 +55,9 @@ static int initiate(vici_conn_t *conn) case 'c': child = arg; continue; + case 'i': + ike = arg; + continue; case 't': timeout = atoi(arg); continue; @@ -80,6 +83,10 @@ static int initiate(vici_conn_t *conn) { vici_add_key_valuef(req, "child", "%s", child); } + if (ike) + { + vici_add_key_valuef(req, "ike", "%s", ike); + } if (timeout) { vici_add_key_valuef(req, "timeout", "%d", timeout * 1000); @@ -121,10 +128,11 @@ static void __attribute__ ((constructor))reg() { command_register((command_t) { initiate, 'i', "initiate", "initiate a connection", - {"--child [--timeout ] [--raw|--pretty]"}, + {"--child [--ike ] [--timeout ] [--raw|--pretty]"}, { {"help", 'h', 0, "show usage information"}, {"child", 'c', 1, "initate a CHILD_SA configuration"}, + {"ike", 'i', 1, "name of the connection to which the child belongs"}, {"timeout", 't', 1, "timeout in seconds before detaching"}, {"raw", 'r', 0, "dump raw response message"}, {"pretty", 'P', 0, "dump raw response message in pretty print"}, diff --git a/src/swanctl/commands/install.c b/src/swanctl/commands/install.c index 59c5c24ab..24a397b87 100644 --- a/src/swanctl/commands/install.c +++ b/src/swanctl/commands/install.c @@ -22,7 +22,7 @@ static int manage_policy(vici_conn_t *conn, char *label) vici_req_t *req; vici_res_t *res; command_format_options_t format = COMMAND_FORMAT_NONE; - char *arg, *child = NULL; + char *arg, *child = NULL, *ike = NULL; int ret = 0; while (TRUE) @@ -40,6 +40,9 @@ static int manage_policy(vici_conn_t *conn, char *label) case 'c': child = arg; continue; + case 'i': + ike = arg; + continue; case EOF: break; default: @@ -52,6 +55,10 @@ static int manage_policy(vici_conn_t *conn, char *label) { vici_add_key_valuef(req, "child", "%s", child); } + if (ike) + { + vici_add_key_valuef(req, "ike", "%s", ike); + } res = vici_submit(req, conn); if (!res) { @@ -98,10 +105,11 @@ static void __attribute__ ((constructor))reg_uninstall() { command_register((command_t) { uninstall, 'u', "uninstall", "uninstall a trap or shunt policy", - {"--child [--raw|--pretty]"}, + {"--child [--ike ] [--raw|--pretty]"}, { {"help", 'h', 0, "show usage information"}, {"child", 'c', 1, "CHILD_SA configuration to uninstall"}, + {"ike", 'i', 1, "name of the connection to which the child belongs"}, {"raw", 'r', 0, "dump raw response message"}, {"pretty", 'P', 0, "dump raw response message in pretty print"}, } @@ -115,10 +123,11 @@ static void __attribute__ ((constructor))reg_install() { command_register((command_t) { install, 'p', "install", "install a trap or shunt policy", - {"--child [--raw|--pretty]"}, + {"--child [--ike ] [--raw|--pretty]"}, { {"help", 'h', 0, "show usage information"}, {"child", 'c', 1, "CHILD_SA configuration to install"}, + {"ike", 'i', 1, "name of the connection to which the child belongs"}, {"raw", 'r', 0, "dump raw response message"}, {"pretty", 'P', 0, "dump raw response message in pretty print"}, } From b657740e16a8e5ccbc6627aef32962211a21220e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 13 Feb 2017 18:18:58 +0100 Subject: [PATCH 18/33] vici: List namespace/peer-cfg name with policies and allow filtering The two names are also transmitted in separate keys. --- src/libcharon/plugins/vici/README.md | 5 ++- src/libcharon/plugins/vici/vici_query.c | 41 +++++++++++++++++-------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index b7b7f4ee0..5c29ed361 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -354,6 +354,7 @@ _list-policy_ events. pass = trap = child = + ike = } => { # completes after streaming list-sa events } @@ -775,7 +776,9 @@ The _list-policy_ event is issued to stream installed policies during an active _list-policies_ command. { - = { + = { + child = + ike = mode = local-ts = [ diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index 692cd7f5d..ba2318a46 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -469,14 +469,19 @@ 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); list_mode(b, child, NULL); @@ -507,16 +512,24 @@ 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); + } list_mode(b, NULL, cfg); @@ -554,25 +567,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); } @@ -580,9 +596,10 @@ CALLBACK(list_policies, vici_message_t*, if (drop || pass) { enumerator = charon->shunts->create_enumerator(charon->shunts); - while (enumerator->enumerate(enumerator, NULL, &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; } @@ -591,13 +608,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: From 2ceeb96db51a36ce0ee04db09dce41f144208f7a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 18 Nov 2016 15:01:18 +0100 Subject: [PATCH 19/33] vici: Add command to load a private key from a token PINs are stored in a "hidden" credential set, so that its shared secrets are not exposed via VICI. Since they are not explicitly loaded as shared secrets via VICI a client might consider them as removed secrets and remove them. --- src/libcharon/plugins/vici/README.md | 18 +++++ src/libcharon/plugins/vici/vici_cred.c | 102 ++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 5c29ed361..556d05e32 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -491,6 +491,24 @@ including keys found in other backends. ] } +### 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 = + slot = + module = + pin = + } => { + success = + errmsg = + id = + } + ### load-shared() ### Load a shared IKE PSK, EAP or XAuth secret into the daemon. diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index c8d71138c..6c7c194c2 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -56,6 +56,11 @@ struct private_vici_cred_t { */ mem_cred_t *creds; + /** + * separate credential set for token PINs + */ + mem_cred_t *pins; + /** * cache CRLs to disk? */ @@ -255,7 +260,7 @@ CALLBACK(unload_key, vici_message_t*, private_vici_cred_t *this, char *name, u_int id, vici_message_t *message) { chunk_t keyid; - char *hex, *msg = NULL; + char buf[BUF_LEN], *hex, *msg = NULL; hex = message->get_str(message, NULL, "id"); if (!hex) @@ -263,8 +268,13 @@ CALLBACK(unload_key, vici_message_t*, return create_reply("key id missing"); } keyid = chunk_from_hex(chunk_from_str(hex), NULL); - DBG1(DBG_CFG, "unloaded private key with id %+B", &keyid); - if (!this->creds->remove_key(this->creds, keyid)) + 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"; } @@ -298,6 +308,87 @@ CALLBACK(get_keys, vici_message_t*, 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) { @@ -474,6 +565,7 @@ static void manage_commands(private_vici_cred_t *this, bool 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); @@ -492,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); } @@ -516,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)) @@ -524,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); From d2e3ff8e0c1a9e1382c9bd7424690c800958c112 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 18 Nov 2016 16:40:34 +0100 Subject: [PATCH 20/33] swanctl: Add `token` secrets for keys on tokens/smartcards --- src/swanctl/commands/load_creds.c | 90 +++++++++++++++++++++++++++++++ src/swanctl/swanctl.opt | 16 ++++++ 2 files changed, 106 insertions(+) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 836017893..848d8512c 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -568,6 +568,93 @@ static void load_containers(load_ctx_t *ctx, char *type, char *dir) } } +/** + * Load a single private key on a token over vici + */ +static bool load_token(load_ctx_t *ctx, char *name, char *pin) +{ + vici_req_t *req; + vici_res_t *res; + enumerator_t *enumerator; + char *key, *value, *id; + bool ret = TRUE; + + req = vici_begin("load-token"); + + enumerator = ctx->cfg->create_key_value_enumerator(ctx->cfg, "secrets.%s", + name); + while (enumerator->enumerate(enumerator, &key, &value)) + { + vici_add_key_valuef(req, key, "%s", value); + } + enumerator->destroy(enumerator); + + if (pin) + { + vici_add_key_valuef(req, "pin", "%s", pin); + } + res = vici_submit(req, ctx->conn); + if (!res) + { + fprintf(stderr, "load-token request failed: %s\n", strerror(errno)); + return FALSE; + } + if (ctx->format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "load-token reply", ctx->format & COMMAND_FORMAT_PRETTY, + stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "loading '%s' failed: %s\n", + name, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + else + { + id = vici_find_str(res, "", "id"); + printf("loaded key %s from token [keyid: %s]\n", name, id); + free(ctx->keys->remove(ctx->keys, id)); + } + vici_free_res(res); + return ret; +} + +/** + * Load keys from tokens + */ +static void load_tokens(load_ctx_t *ctx) +{ + enumerator_t *enumerator; + char *section, *pin = NULL, prompt[128]; + + enumerator = ctx->cfg->create_section_enumerator(ctx->cfg, "secrets"); + while (enumerator->enumerate(enumerator, §ion)) + { + if (strpfx(section, "token")) + { + if (!ctx->noprompt && + !ctx->cfg->get_str(ctx->cfg, "secrets.%s.pin", NULL, section)) + { +#ifdef HAVE_GETPASS + snprintf(prompt, sizeof(prompt), "PIN for %s: ", section); + pin = strdupnull(getpass(prompt)); +#endif + } + load_token(ctx, section, pin); + if (pin) + { + memwipe(pin, strlen(pin)); + free(pin); + pin = NULL; + } + } + } + enumerator->destroy(enumerator); +} + + + /** * Load a single secret over VICI */ @@ -591,6 +678,7 @@ static bool load_secret(load_ctx_t *ctx, char *section) "bliss", "pkcs8", "pkcs12", + "token", }; for (i = 0; i < countof(types); i++) @@ -842,6 +930,8 @@ int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, load_containers(&ctx, "pkcs12", SWANCTL_PKCS12DIR); + load_tokens(&ctx); + enumerator = cfg->create_section_enumerator(cfg, "secrets"); while (enumerator->enumerate(enumerator, §ion)) { diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 8ddc9f750..caae41e88 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -918,6 +918,22 @@ secrets.pkcs12.file = secrets.pkcs12.secret Value of decryption passphrase for PKCS#12 container. +secrets.token { # } + Definition for a private key that's stored on a token/smartcard. + +secrets.token.handle = + Hex-encoded CKA_ID of the private key on the token. + +secrets.token.slot = + Optional slot number to access the token. + +secrets.token.module = + Optional PKCS#11 module name to access the token. + +secrets.token.pin = + Optional PIN required to access the key on the token. If none is provided + the user is prompted during an interactive --load-creds call. + pools { # } Section defining named pools. From 00bf6a2a492308874c86f909796c5871b94c0568 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 30 Nov 2016 12:44:51 +0100 Subject: [PATCH 21/33] vici: Add support to load certificates from tokens --- src/libcharon/plugins/vici/vici_config.c | 127 ++++++++++++++++++++--- src/swanctl/swanctl.opt | 48 +++++++++ 2 files changed, 163 insertions(+), 12 deletions(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 3b27bf7c3..0a8fc2905 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -246,6 +246,26 @@ typedef struct { vici_message_t *reply; } request_data_t; +/** + * Certificate data + */ +typedef struct { + request_data_t *request; + char *handle; + uint32_t slot; + char *module; +} cert_data_t; + +/** + * Clean up certificate data + */ +static void free_cert_data(cert_data_t *data) +{ + free(data->handle); + free(data->module); + free(data); +} + /** * Auth config data */ @@ -1160,28 +1180,37 @@ CALLBACK(parse_cert_policy, bool, 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) { - vici_authority_t *authority; - vici_cred_t *cred; 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; } @@ -1366,6 +1395,19 @@ CALLBACK(parse_hosts, bool, return TRUE; } +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 }, + }; + + 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) { @@ -1492,6 +1534,67 @@ CALLBACK(peer_kv, bool, &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) + { + auth->request->reply = create_reply("CKA_ID missing: %s", name); + free_cert_data(data); + return FALSE; + } + + 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 */ @@ -1654,7 +1757,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; diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index caae41e88..0bf1243d0 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -292,6 +292,22 @@ connections..local.certs = certificate request payloads. If no appropriate CA can be located, the first certificate is used. +connections..local.cert = + Section for a certificate candidate to use for authentication. + + Section for a certificate candidate to use for authentication. Certificates + in _certs_ are transmitted as binary blobs, these sections offer more + flexibility. + +connections..local.cert.handle = + Hex-encoded CKA_ID of the certificate on a token. + +connections..local.cert.slot = + Optional slot number of the token that stores the certificate. + +connections..local.cert.module = + Optional PKCS#11 module name. + connections..local.pubkeys = Comma separated list of raw public key candidates to use for authentication. @@ -419,6 +435,22 @@ connections..remote.certs = The certificates may use a relative path from the **swanctl** _x509_ directory or an absolute path. +connections..remote.cert = + Section for a certificate to accept for authentication. + + Section for a certificate to accept for authentication. Certificates + in _certs_ are transmitted as binary blobs, these sections offer more + flexibility. + +connections..remote.cert.handle = + Hex-encoded CKA_ID of the certificate on a token. + +connections..remote.cert.slot = + Optional slot number of the token that stores the certificate. + +connections..remote.cert.module = + Optional PKCS#11 module name. + connections..remote.cacerts = Comma separated list of CA certificates to accept for authentication. @@ -426,6 +458,22 @@ connections..remote.cacerts = The certificates may use a relative path from the **swanctl** _x509ca_ directory or an absolute path. +connections..remote.cacert = + Section for a CA certificate to accept for authentication. + + Section for a CA certificate to accept for authentication. Certificates + in _cacerts_ are transmitted as binary blobs, these sections offer more + flexibility. + +connections..remote.cacert.handle = + Hex-encoded CKA_ID of the CA certificate on a token. + +connections..remote.cacert.slot = + Optional slot number of the token that stores the CA certificate. + +connections..remote.cacert.module = + Optional PKCS#11 module name. + connections..remote.pubkeys = Comma separated list of raw public keys to accept for authentication. From 2f8354ca6c0877632327ed3fff532a9b381a4d56 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 30 Nov 2016 15:09:04 +0100 Subject: [PATCH 22/33] vici: Add support to load certificates from file paths Probably not that useful via swanctl.conf but could be when used via VICI. --- src/libcharon/plugins/vici/vici_config.c | 45 +++++++++++++++++------- src/swanctl/swanctl.opt | 36 +++++++++++++++++++ 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 0a8fc2905..b9f6b79b1 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -254,6 +254,7 @@ typedef struct { char *handle; uint32_t slot; char *module; + char *file; } cert_data_t; /** @@ -263,6 +264,7 @@ static void free_cert_data(cert_data_t *data) { free(data->handle); free(data->module); + free(data->file); free(data); } @@ -1402,6 +1404,7 @@ CALLBACK(cert_kv, bool, { "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, @@ -1556,30 +1559,46 @@ CALLBACK(auth_sn, bool, free_cert_data(data); return FALSE; } - if (!data->handle) + if (!data->handle && !data->file) { - auth->request->reply = create_reply("CKA_ID missing: %s", name); + 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; } - handle = chunk_from_hex(chunk_from_str(data->handle), NULL); - if (data->slot != -1) + if (data->file) { 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); + BUILD_FROM_FILE, data->file, 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); + 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); } - chunk_free(&handle); free_cert_data(data); if (!cert) { diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 0bf1243d0..f749564ea 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -299,9 +299,21 @@ connections..local.cert = in _certs_ are transmitted as binary blobs, these sections offer more flexibility. +connections..local.cert.file = + Absolute path to the certificate to load. + + Absolute path to the certificate to load. Passed as-is to the daemon, so it + must be readable by it. + + Configure either this or _handle_, but not both, in one section. + connections..local.cert.handle = Hex-encoded CKA_ID of the certificate on a token. + Hex-encoded CKA_ID of the certificate on a token. + + Configure either this or _file_, but not both, in one section. + connections..local.cert.slot = Optional slot number of the token that stores the certificate. @@ -442,9 +454,21 @@ connections..remote.cert = in _certs_ are transmitted as binary blobs, these sections offer more flexibility. +connections..remote.cert.file = + Absolute path to the certificate to load. + + Absolute path to the certificate to load. Passed as-is to the daemon, so it + must be readable by it. + + Configure either this or _handle_, but not both, in one section. + connections..remote.cert.handle = Hex-encoded CKA_ID of the certificate on a token. + Hex-encoded CKA_ID of the certificate on a token. + + Configure either this or _file_, but not both, in one section. + connections..remote.cert.slot = Optional slot number of the token that stores the certificate. @@ -465,9 +489,21 @@ connections..remote.cacert = in _cacerts_ are transmitted as binary blobs, these sections offer more flexibility. +connections..remote.cacert.file = + Absolute path to the certificate to load. + + Absolute path to the certificate to load. Passed as-is to the daemon, so it + must be readable by it. + + Configure either this or _handle_, but not both, in one section. + connections..remote.cacert.handle = Hex-encoded CKA_ID of the CA certificate on a token. + Hex-encoded CKA_ID of the CA certificate on a token. + + Configure either this or _file_, but not both, in one section. + connections..remote.cacert.slot = Optional slot number of the token that stores the CA certificate. From bd6ef6be7ead12a5241b7be9447067069e4ebae3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 30 Nov 2016 15:41:18 +0100 Subject: [PATCH 23/33] vici: Add support to load CA certificates from tokens and paths in authority sections --- src/libcharon/plugins/vici/vici_authority.c | 107 ++++++++++++++++++-- src/swanctl/commands/load_authorities.c | 8 +- src/swanctl/swanctl.opt | 36 +++++-- 3 files changed, 130 insertions(+), 21 deletions(-) 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,8 +200,27 @@ 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 */ @@ -216,6 +236,28 @@ CALLBACK(parse_string, bool, return TRUE; } +/** + * 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 */ @@ -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)) { - authority_destroy(data.authority); + free_load_data(data); return FALSE; } - log_authority_data(data.authority); + 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) + { + request->reply = create_reply("CA certificate missing: %s", name); + free_load_data(data); + return FALSE; + } + 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/swanctl/commands/load_authorities.c b/src/swanctl/commands/load_authorities.c index 352a185e8..8947866f5 100644 --- a/src/swanctl/commands/load_authorities.c +++ b/src/swanctl/commands/load_authorities.c @@ -86,18 +86,18 @@ static bool add_key_values(vici_req_t *req, settings_t *cfg, char *section) enumerator = cfg->create_key_value_enumerator(cfg, section); while (enumerator->enumerate(enumerator, &key, &value)) { - /* pool subnet is encoded as key/value, all other attributes as list */ if (streq(key, "cacert")) { ret = add_file_key_value(req, key, value); } - else if (streq(key, "cert_uri_base")) + else if (streq(key, "crl_uris") || + streq(key, "ocsp_uris")) { - vici_add_key_valuef(req, key, "%s", value); + add_list_key(req, key, value); } else { - add_list_key(req, key, value); + vici_add_key_valuef(req, key, "%s", value); } if (!ret) { diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index f749564ea..7eb0885c8 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -1054,18 +1054,40 @@ authorities. { # } authorities..cacert = CA certificate belonging to the certification authority. - The certificates may use a relative path from the **swanctl** _x509ca_ - directory or an absolute path. + CA certificate belonging to the certification authority. The certificates + may use a relative path from the **swanctl** _x509ca_ directory or an + absolute path. + + Configure one of _cacert_, _file_, or _handle_ per section. + +authorities..file = + Absolute path to the certificate to load. + + Absolute path to the certificate to load. Passed as-is to the daemon, so it + must be readable by it. + + Configure one of _cacert_, _file_, or _handle_ per section. + +authorities..handle = + Hex-encoded CKA_ID of the CA certificate on a token. + + Hex-encoded CKA_ID of the CA certificate on a token. + + Configure one of _cacert_, _file_, or _handle_ per section. + +authorities..slot = + Optional slot number of the token that stores the CA certificate. + +authorities..module = + Optional PKCS#11 module name. authorities..crl_uris = - Comma-separated list of CRL distribution points + Comma-separated list of CRL distribution points. - Comma-separated list of CRL distribution points (ldap, http, or file URI) + Comma-separated list of CRL distribution points (ldap, http, or file URI). authorities..ocsp_uris = - Comma-separated list of OCSP URIs - - Comma-separated list of OCSP URIs + Comma-separated list of OCSP URIs. authorities..cert_uri_base = Defines the base URI for the Hash and URL feature supported by IKEv2. From 75665375b73071573cee5e10be2f9cb69551028e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 18 Jan 2017 17:46:27 +0100 Subject: [PATCH 24/33] swanctl: Allow specifying pubkeys directly via 0x/0s prefix --- src/swanctl/commands/load_conns.c | 72 ++++++++++++++++++------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 82592f456..0518ef54f 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -98,7 +98,7 @@ static void add_list_key(vici_req_t *req, char *key, char *value) static bool add_file_list_key(vici_req_t *req, char *key, char *value) { enumerator_t *enumerator; - chunk_t *map; + chunk_t *map, blob; char *token, buf[PATH_MAX]; bool ret = TRUE; @@ -106,40 +106,50 @@ static bool add_file_list_key(vici_req_t *req, char *key, char *value) enumerator = enumerator_create_token(value, ",", " "); while (enumerator->enumerate(enumerator, &token)) { - if (!path_absolute(token)) + if (strcasepfx(token, "0x") || strcasepfx(token, "0s")) { - if (streq(key, "certs")) - { - snprintf(buf, sizeof(buf), "%s%s%s", - SWANCTL_X509DIR, DIRECTORY_SEPARATOR, token); - token = buf; - } - else if (streq(key, "cacerts")) - { - snprintf(buf, sizeof(buf), "%s%s%s", - SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, token); - token = buf; - } - else if (streq(key, "pubkeys")) - { - snprintf(buf, sizeof(buf), "%s%s%s", - SWANCTL_PUBKEYDIR, DIRECTORY_SEPARATOR, token); - token = buf; - } - } - - map = chunk_map(token, FALSE); - if (map) - { - vici_add_list_item(req, map->ptr, map->len); - chunk_unmap(map); + blob = chunk_from_str(token + 2); + blob = strcasepfx(token, "0x") ? chunk_from_hex(blob, NULL) + : chunk_from_base64(blob, NULL); + vici_add_list_item(req, blob.ptr, blob.len); + chunk_free(&blob); } else { - fprintf(stderr, "loading %s certificate '%s' failed: %s\n", - key, token, strerror(errno)); - ret = FALSE; - break; + if (!path_absolute(token)) + { + if (streq(key, "certs")) + { + snprintf(buf, sizeof(buf), "%s%s%s", + SWANCTL_X509DIR, DIRECTORY_SEPARATOR, token); + token = buf; + } + else if (streq(key, "cacerts")) + { + snprintf(buf, sizeof(buf), "%s%s%s", + SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, token); + token = buf; + } + else if (streq(key, "pubkeys")) + { + snprintf(buf, sizeof(buf), "%s%s%s", + SWANCTL_PUBKEYDIR, DIRECTORY_SEPARATOR, token); + token = buf; + } + } + map = chunk_map(token, FALSE); + if (map) + { + vici_add_list_item(req, map->ptr, map->len); + chunk_unmap(map); + } + else + { + fprintf(stderr, "loading %s certificate '%s' failed: %s\n", + key, token, strerror(errno)); + ret = FALSE; + break; + } } } enumerator->destroy(enumerator); From 04c0219e55d9338b6492548c073189bfd3d5431b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 9 Dec 2016 14:45:41 +0100 Subject: [PATCH 25/33] vici: Use unique names for CHILD_SAs in the list-sas command The original name is returned in the new "name" attribute. This fixes an issue with bindings that map VICI messages to dictionaries. For instance, in roadwarrior scenarios where every CHILD_SA has the same name only the information of the last CHILD_SA would end up in the dictionary for that name. --- src/libcharon/plugins/vici/README.md | 3 ++- src/libcharon/plugins/vici/vici_query.c | 6 +++++- src/swanctl/commands/list_sas.c | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 556d05e32..0a06e5d7c 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -746,7 +746,8 @@ command. ] child-sas = { - * = { + * = { + name = uniqueid = reqid = state = diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index ba2318a46..c60b88946 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -128,6 +128,7 @@ 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)); @@ -420,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"); @@ -448,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); } diff --git a/src/swanctl/commands/list_sas.c b/src/swanctl/commands/list_sas.c index 4257c83a5..28602fc65 100644 --- a/src/swanctl/commands/list_sas.c +++ b/src/swanctl/commands/list_sas.c @@ -112,8 +112,9 @@ CALLBACK(child_sas, int, if (ret == 0) { printf(" %s: #%s, reqid %s, %s, %s%s, %s:", - name, child->get(child, "uniqueid"), child->get(child, "reqid"), - child->get(child, "state"), child->get(child, "mode"), + child->get(child, "name"), child->get(child, "uniqueid"), + child->get(child, "reqid"), child->get(child, "state"), + child->get(child, "mode"), child->get(child, "encap") ? "-in-UDP" : "", child->get(child, "protocol")); From 808472c9f936e55a01d252169fd863853554cf25 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 24 Jan 2017 16:26:48 +0100 Subject: [PATCH 26/33] vici: Add command to initiate SA rekeying --- src/libcharon/plugins/vici/README.md | 18 ++++ src/libcharon/plugins/vici/vici_control.c | 102 +++++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 0a06e5d7c..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 = } => { success = + matches = + terminated = errmsg = } 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 = + ike = + child-id = + ike-id = + } => { + success = + matches = + errmsg = + } + ### 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 = + matches = errmsg = } diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 05d0dc5a6..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 #include +#include +#include #include 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); @@ -671,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); From e2d997121550bd09ae71a89616e5926d9ab6f293 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 24 Jan 2017 16:34:32 +0100 Subject: [PATCH 27/33] swanctl: Add --rekey command --- src/swanctl/Makefile.am | 1 + src/swanctl/command.h | 2 +- src/swanctl/commands/rekey.c | 125 +++++++++++++++++++++++++++++++++++ src/swanctl/swanctl.8.in | 3 + 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/swanctl/commands/rekey.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 9ca759ea3..2fc998262 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -4,6 +4,7 @@ swanctl_SOURCES = \ command.c command.h \ commands/initiate.c \ commands/terminate.c \ + commands/rekey.c \ commands/redirect.c \ commands/install.c \ commands/list_sas.c \ diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 7b92ae91a..c17811498 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 24 +#define MAX_COMMANDS 25 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/rekey.c b/src/swanctl/commands/rekey.c new file mode 100644 index 000000000..47a313657 --- /dev/null +++ b/src/swanctl/commands/rekey.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 Tobias Brunner + * 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 + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "command.h" + +#include + +static int rekey(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + command_format_options_t format = COMMAND_FORMAT_NONE; + char *arg, *child = NULL, *ike = NULL; + int ret = 0, child_id = 0, ike_id = 0; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'P': + format |= COMMAND_FORMAT_PRETTY; + /* fall through to raw */ + case 'r': + format |= COMMAND_FORMAT_RAW; + continue; + case 'c': + child = arg; + continue; + case 'i': + ike = arg; + continue; + case 'C': + child_id = atoi(arg); + continue; + case 'I': + ike_id = atoi(arg); + continue; + case EOF: + break; + default: + return command_usage("invalid --rekey option"); + } + break; + } + + req = vici_begin("rekey"); + if (child) + { + vici_add_key_valuef(req, "child", "%s", child); + } + if (ike) + { + vici_add_key_valuef(req, "ike", "%s", ike); + } + if (child_id) + { + vici_add_key_valuef(req, "child-id", "%d", child_id); + } + if (ike_id) + { + vici_add_key_valuef(req, "ike-id", "%d", ike_id); + } + res = vici_submit(req, conn); + if (!res) + { + ret = errno; + fprintf(stderr, "rekey request failed: %s\n", strerror(errno)); + return ret; + } + if (format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "rekey reply", format & COMMAND_FORMAT_PRETTY, + stdout); + } + else + { + if (streq(vici_find_str(res, "no", "success"), "yes")) + { + printf("rekey completed successfully\n"); + } + else + { + fprintf(stderr, "rekey failed: %s\n", + vici_find_str(res, "", "errmsg")); + ret = 1; + } + } + vici_free_res(res); + return ret; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + rekey, 'R', "rekey", "rekey an SA", + {"--child | --ike | --ike-id ", + "[--raw|--pretty]"}, + { + {"help", 'h', 0, "show usage information"}, + {"child", 'c', 1, "rekey by CHILD_SA name"}, + {"ike", 'i', 1, "rekey by IKE_SA name"}, + {"child-id", 'C', 1, "rekey by CHILD_SA unique identifier"}, + {"ike-id", 'I', 1, "rekey by IKE_SA unique identifier"}, + {"raw", 'r', 0, "dump raw response message"}, + {"pretty", 'P', 0, "dump raw response message in pretty print"}, + } + }); +} diff --git a/src/swanctl/swanctl.8.in b/src/swanctl/swanctl.8.in index 9c5a5a03d..391fe486f 100644 --- a/src/swanctl/swanctl.8.in +++ b/src/swanctl/swanctl.8.in @@ -40,6 +40,9 @@ initiate a connection .B "\-t, \-\-terminate" terminate a connection .TP +.B "\-R, \-\-rekey" +rekey an SA +.TP .B "\-d, \-\-redirect" redirect an IKE_SA .TP From ec5f127a45c7c98eb0992d202bda1629e387469c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 1 Feb 2017 11:02:22 +0100 Subject: [PATCH 28/33] vici: Include uniqueness policy in list-conns --- src/libcharon/plugins/vici/vici_query.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index c60b88946..c0f4e2de9 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -790,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); From ed96fe72cf8c741427b833c6f284a8857d8371a7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 7 Feb 2017 11:30:49 +0100 Subject: [PATCH 29/33] peer-cfg: Store mediated_by as name and not peer-cfg reference This way updates to the mediation config are respected and the order in which configs are configured/loaded does not matter. The SQL plugin currently maintains the strong relationship between mediated and mediation connection (we could theoretically change that to a string too). --- src/libcharon/config/peer_cfg.c | 12 +-- src/libcharon/config/peer_cfg.h | 16 ++-- src/libcharon/plugins/medcli/medcli_config.c | 81 ++++++++++++------- src/libcharon/plugins/sql/sql_config.c | 10 ++- src/libcharon/plugins/stroke/stroke_config.c | 23 +----- .../processing/jobs/initiate_mediation_job.c | 21 ++++- 6 files changed, 95 insertions(+), 68 deletions(-) diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index 5665b66c4..5d7ab076e 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2016 Tobias Brunner + * Copyright (C) 2007-2017 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -164,7 +164,7 @@ struct private_peer_cfg_t { /** * Name of the mediation connection to mediate through */ - peer_cfg_t *mediated_by; + char *mediated_by; /** * ID of our peer at the mediation server (= leftid of the peer's conn with @@ -580,7 +580,7 @@ METHOD(peer_cfg_t, is_mediation, bool, return this->mediation; } -METHOD(peer_cfg_t, get_mediated_by, peer_cfg_t*, +METHOD(peer_cfg_t, get_mediated_by, char*, private_peer_cfg_t *this) { return this->mediated_by; @@ -683,7 +683,7 @@ METHOD(peer_cfg_t, equals, bool, auth_cfg_equal(this, other) #ifdef ME && this->mediation == other->mediation && - this->mediated_by == other->mediated_by && + streq(this->mediated_by, other->mediated_by) && (this->peer_id == other->peer_id || (this->peer_id && other->peer_id && this->peer_id->equals(this->peer_id, other->peer_id))) @@ -713,8 +713,8 @@ METHOD(peer_cfg_t, destroy, void, this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->pools->destroy_function(this->pools, free); #ifdef ME - DESTROY_IF(this->mediated_by); DESTROY_IF(this->peer_id); + free(this->mediated_by); #endif /* ME */ this->mutex->destroy(this->mutex); free(this->name); @@ -802,7 +802,7 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .refcount = 1, #ifdef ME .mediation = data->mediation, - .mediated_by = data->mediated_by, + .mediated_by = strdupnull(data->mediated_by), .peer_id = data->peer_id, #endif /* ME */ ); diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 8e4d5331c..b294ae72f 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2016 Tobias Brunner + * Copyright (C) 2007-2017 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -319,14 +319,14 @@ struct peer_cfg_t { * * @return TRUE, if this is a mediation connection */ - bool (*is_mediation) (peer_cfg_t *this); + bool (*is_mediation)(peer_cfg_t *this); /** - * Get peer_cfg of the connection this one is mediated through. + * Get name of the connection this one is mediated through. * - * @return the peer_cfg of the mediation connection + * @return the name of the mediation connection */ - peer_cfg_t* (*get_mediated_by) (peer_cfg_t *this); + char* (*get_mediated_by)(peer_cfg_t *this); /** * Get the id of the other peer at the mediation server. @@ -338,7 +338,7 @@ struct peer_cfg_t { * * @return the id of the other peer */ - identification_t* (*get_peer_id) (peer_cfg_t *this); + identification_t* (*get_peer_id)(peer_cfg_t *this); #endif /* ME */ /** @@ -398,8 +398,8 @@ struct peer_cfg_create_t { #ifdef ME /** TRUE if this is a mediation connection */ bool mediation; - /** peer_cfg_t of the mediation connection to mediate through (adopted) */ - peer_cfg_t *mediated_by; + /** peer_cfg_t of the mediation connection to mediate through (cloned) */ + char *mediated_by; /** ID that identifies our peer at the mediation server (adopted) */ identification_t *peer_id; #endif /* ME */ 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 @@ -23,6 +23,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 */ @@ -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/processing/jobs/initiate_mediation_job.c b/src/libcharon/processing/jobs/initiate_mediation_job.c index 6c01ffe95..1082eae0b 100644 --- a/src/libcharon/processing/jobs/initiate_mediation_job.c +++ b/src/libcharon/processing/jobs/initiate_mediation_job.c @@ -82,8 +82,25 @@ METHOD(job_t, initiate, job_requeue_t, charon->ike_sa_manager->checkin(charon->ike_sa_manager, mediated_sa); - mediation_cfg = mediated_cfg->get_mediated_by(mediated_cfg); - mediation_cfg->get_ref(mediation_cfg); + mediation_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, + mediated_cfg->get_mediated_by(mediated_cfg)); + if (!mediation_cfg) + { + DBG1(DBG_IKE, "mediation connection '%s' not found, aborting", + mediated_cfg->get_mediated_by(mediated_cfg)); + mediated_cfg->destroy(mediated_cfg); + return JOB_REQUEUE_NONE; + } + if (!mediation_cfg->is_mediation(mediation_cfg)) + { + DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is no " + "mediation connection, aborting", + mediated_cfg->get_mediated_by(mediated_cfg), + mediated_cfg->get_name(mediated_cfg)); + mediated_cfg->destroy(mediated_cfg); + mediation_cfg->destroy(mediation_cfg); + return JOB_REQUEUE_NONE; + } enumerator = mediation_cfg->create_auth_cfg_enumerator(mediation_cfg, TRUE); From f927ba975b95bd10b7ecc6caed3088e38125402c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 7 Feb 2017 12:04:30 +0100 Subject: [PATCH 30/33] vici: Add support for mediation extension --- src/libcharon/plugins/vici/vici_config.c | 86 +++++++++++++++++++++++- src/swanctl/swanctl.opt | 24 +++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index b9f6b79b1..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 * @@ -318,6 +318,11 @@ typedef struct { 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; /** @@ -405,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)) { @@ -449,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 */ } /** @@ -1397,6 +1414,24 @@ 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) { @@ -1531,6 +1566,11 @@ 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, @@ -2260,6 +2300,42 @@ 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, @@ -2281,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/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 7eb0885c8..cd2d9142d 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -264,6 +264,30 @@ connections..pools = other configuration attributes from. Each name references a pool by name from either the **pools** section or an external pool. +connections..mediation = no + Whether this connection is a mediation connection. + + Whether this connection is a mediation connection, that is, whether this + connection is used to mediate other connections using the IKEv2 Mediation + Extension. Mediation connections create no CHILD_SA. + +connections..mediated_by = + The name of the connection to mediate this connection through. + + The name of the connection to mediate this connection through. If given, the + connection will be mediated through the named mediation connection. + The mediation connection must have **mediation** enabled. + +connections..mediation_peer = + Identity under which the peer is registered at the mediation server. + + Identity under which the peer is registered at the mediation server, that + is, the IKE identity the other end of this connection uses as its local + identity on its connection to the mediation server. This is the identity we + request the mediation server to mediate us with. Only relevant on + connections that set **mediated_by**. If it is not given, the remote IKE + identity of the first authentication round of this connection will be used. + connections..local {} Section for a local authentication round. From fa5f6ba26c0be128f9220ccd6409309c85188456 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 8 Feb 2017 15:20:58 +0100 Subject: [PATCH 31/33] vici: Let has_event_listeners() actually check if clients are registered Fixes: 8d96f90a7983 ("vici: Add function to test if an event should be generated") --- src/libcharon/plugins/vici/vici_dispatcher.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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); From 8bd8dcd52264fb7f3611dd254090491dc9f94a2d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 13 Feb 2017 17:38:49 +0100 Subject: [PATCH 32/33] vici: Only log messages if there actually is a listener --- src/libcharon/plugins/vici/vici_logger.c | 7 +++++++ 1 file changed, 7 insertions(+) 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; } From e16d1005f7c14b261a4d91f63159f96688fd8f00 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 15 Feb 2017 17:49:06 +0100 Subject: [PATCH 33/33] NEWS: VICI updates --- NEWS | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 5edbbf810..bb2a80a32 100644 --- a/NEWS +++ b/NEWS @@ -8,9 +8,23 @@ strongswan-5.5.2 draft-ietf-ipsecme-eddsa. Ed25519-based public key pairs, X.509 certificates and CRLs can be generated and printed by the pki tool. -- In-place update of cached base and delta CRLs does no leave dozens +- In-place update of cached base and delta CRLs does not leave dozens of stale copies in cache memory. +- Several new features for the VICI interface and the swanctl utility: Querying + specific pools, enumerating and unloading keys and shared secrets, loading + keys and certificates from PKCS#11 tokens, the ability to initiate, install + and uninstall connections and policies by their exact name (if multiple child + sections in different connections share the same name), a command to initiate + the rekeying of IKE and IPsec SAs, support for settings previously only + supported by the old config files (plain pubkeys, dscp, certificate policies, + IPv6 Transport Proxy Mode, NT Hash secrets, mediation extension). + + Important: Due to issues with VICI bindings that map sub-sections to + dictionaries the CHILD_SA sections returned via list-sas now have a unique + name, the original name of a CHILD_SA is returned in the "name" key of its + section. + strongswan-5.5.1 ----------------