From 4c56c4621be29dbd8f96f757b8b56ac97d5792f4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 17:24:32 +0100 Subject: [PATCH 01/38] libcharon: Execute scripts defined in strongswan.conf during startup/shutdown --- src/libcharon/daemon.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index 0cecd1d3b..16babf014 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "daemon.h" @@ -476,6 +477,53 @@ static void destroy(private_daemon_t *this) free(this); } +/** + * Run a set of configured scripts + */ +static void run_scripts(private_daemon_t *this, char *verb) +{ + enumerator_t *enumerator; + char *key, *value, *pos, buf[1024]; + FILE *cmd; + + enumerator = lib->settings->create_key_value_enumerator(lib->settings, + "%s.%s-scripts", lib->ns, verb); + while (enumerator->enumerate(enumerator, &key, &value)) + { + DBG1(DBG_DMN, "executing %s script '%s' (%s):", verb, key, value); + cmd = popen(value, "r"); + if (!cmd) + { + DBG1(DBG_DMN, "executing %s script '%s' (%s) failed: %s", + verb, key, value, strerror(errno)); + continue; + } + while (TRUE) + { + if (!fgets(buf, sizeof(buf), cmd)) + { + if (ferror(cmd)) + { + DBG1(DBG_DMN, "reading from %s script '%s' (%s) failed", + verb, key, value); + } + break; + } + else + { + pos = buf + strlen(buf); + if (pos > buf && pos[-1] == '\n') + { + pos[-1] = '\0'; + } + DBG1(DBG_DMN, "%s: %s", key, buf); + } + } + pclose(cmd); + } + enumerator->destroy(enumerator); +} + METHOD(daemon_t, start, void, private_daemon_t *this) { @@ -483,6 +531,8 @@ METHOD(daemon_t, start, void, lib->processor->set_threads(lib->processor, lib->settings->get_int(lib->settings, "%s.threads", DEFAULT_THREADS, lib->ns)); + + run_scripts(this, "start"); } @@ -598,6 +648,8 @@ void libcharon_deinit() return; } + run_scripts(this, "stop"); + destroy(this); charon = NULL; } From e381e69f9bcfa0748a5726607046815ea5b43ad4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 29 Jan 2014 14:37:32 +0100 Subject: [PATCH 02/38] swanctl: Add a stub for a vici based configuration and control utility --- configure.ac | 7 + src/Makefile.am | 4 + src/checksum/Makefile.am | 4 + src/swanctl/.gitignore | 1 + src/swanctl/Makefile.am | 16 ++ src/swanctl/command.c | 309 +++++++++++++++++++++++++++++++++++++++ src/swanctl/command.h | 98 +++++++++++++ src/swanctl/swanctl.c | 57 ++++++++ 8 files changed, 496 insertions(+) create mode 100644 src/swanctl/.gitignore create mode 100644 src/swanctl/Makefile.am create mode 100644 src/swanctl/command.c create mode 100644 src/swanctl/command.h create mode 100644 src/swanctl/swanctl.c diff --git a/configure.ac b/configure.ac index 6dbcccf37..336057a92 100644 --- a/configure.ac +++ b/configure.ac @@ -265,6 +265,7 @@ ARG_ENABL_SET([medcli], [enable mediation client configuration database ARG_ENABL_SET([medsrv], [enable mediation server web frontend and daemon plugin.]) ARG_ENABL_SET([nm], [enable NetworkManager backend.]) ARG_DISBL_SET([scripts], [disable additional utilities (found in directory scripts).]) +ARG_ENABL_SET([swanctl], [enable swanctl configuration and control tool.]) ARG_ENABL_SET([tkm], [enable Trusted Key Manager support.]) ARG_DISBL_SET([tools], [disable additional utilities (scepclient and pki).]) ARG_ENABL_SET([aikgen], [enable AIK generator.]) @@ -399,6 +400,10 @@ if test x$fips_prf = xtrue; then fi fi +if test x$swanctl = xtrue; then + vici=true +fi + if test x$smp = xtrue -o x$tnccs_11 = xtrue -o x$tnc_ifmap = xtrue; then xml=true fi @@ -1389,6 +1394,7 @@ AM_CONDITIONAL(COVERAGE, test x$coverage = xtrue) AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue) AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue) AM_CONDITIONAL(USE_AIKGEN, test x$aikgen = xtrue) +AM_CONDITIONAL(USE_SWANCTL, test x$swanctl = xtrue) # ======================== # set global definitions @@ -1605,6 +1611,7 @@ AC_CONFIG_FILES([ src/checksum/Makefile src/conftest/Makefile src/pt-tls-client/Makefile + src/swanctl/Makefile scripts/Makefile testing/Makefile ]) diff --git a/src/Makefile.am b/src/Makefile.am index e76eb4398..38e4b834d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,10 @@ if USE_TOOLS SUBDIRS += scepclient pki endif +if USE_SWANCTL + SUBDIRS += swanctl +endif + if USE_CONFTEST SUBDIRS += conftest endif diff --git a/src/checksum/Makefile.am b/src/checksum/Makefile.am index 82bbadcf1..078c59790 100644 --- a/src/checksum/Makefile.am +++ b/src/checksum/Makefile.am @@ -104,6 +104,10 @@ if USE_TOOLS exes += $(DESTDIR)$(bindir)/pki endif +if USE_SWANCTL + exes += $(DESTDIR)$(sbindir)/swanctl +endif + if USE_ATTR_SQL exes += $(DESTDIR)$(ipsecdir)/pool endif diff --git a/src/swanctl/.gitignore b/src/swanctl/.gitignore new file mode 100644 index 000000000..1db645ba7 --- /dev/null +++ b/src/swanctl/.gitignore @@ -0,0 +1 @@ +swanctl diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am new file mode 100644 index 000000000..a63c8d9f7 --- /dev/null +++ b/src/swanctl/Makefile.am @@ -0,0 +1,16 @@ +sbin_PROGRAMS = swanctl + +swanctl_SOURCES = \ + command.c command.h \ + swanctl.c + +swanctl_LDADD = \ + $(top_builddir)/src/libcharon/plugins/vici/libvici.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la + +swanctl.o : $(top_builddir)/config.status + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon/plugins/vici \ + -DPLUGINS=\""${s_plugins}\"" diff --git a/src/swanctl/command.c b/src/swanctl/command.c new file mode 100644 index 000000000..29f6be97f --- /dev/null +++ b/src/swanctl/command.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2009 Martin Willi + * 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" + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * Registered commands. + */ +static command_t cmds[MAX_COMMANDS]; + +/** + * active command. + */ +static int active = 0; + +/** + * number of registered commands + */ +static int registered = 0; + +/** + * help command index + */ +static int help_idx; + +/** + * Uri to connect to + */ +static char *uri = NULL; + +static int argc; + +static char **argv; + +static options_t *options; + +/** + * Global options used by all subcommands + */ +static struct option command_opts[MAX_COMMANDS > MAX_OPTIONS ? + MAX_COMMANDS : MAX_OPTIONS]; + +/** + * Global optstring used by all subcommands + */ +static char command_optstring[(MAX_COMMANDS > MAX_OPTIONS ? + MAX_COMMANDS : MAX_OPTIONS) * 3]; + +/** + * Build command_opts/command_optstr for the active command + */ +static void build_opts() +{ + int i, pos = 0; + + memset(command_opts, 0, sizeof(command_opts)); + memset(command_optstring, 0, sizeof(command_optstring)); + if (active == help_idx) + { + for (i = 0; cmds[i].cmd; i++) + { + command_opts[i].name = cmds[i].cmd; + command_opts[i].val = cmds[i].op; + command_optstring[i] = cmds[i].op; + } + } + else + { + for (i = 0; cmds[active].options[i].name; i++) + { + command_opts[i].name = cmds[active].options[i].name; + command_opts[i].has_arg = cmds[active].options[i].arg; + command_opts[i].val = cmds[active].options[i].op; + command_optstring[pos++] = cmds[active].options[i].op; + switch (cmds[active].options[i].arg) + { + case optional_argument: + command_optstring[pos++] = ':'; + /* FALL */ + case required_argument: + command_optstring[pos++] = ':'; + /* FALL */ + case no_argument: + default: + break; + } + } + } +} + +/** + * getopt_long wrapper + */ +int command_getopt(char **arg) +{ + int op; + + while (TRUE) + { + op = getopt_long(argc, argv, command_optstring, command_opts, NULL); + switch (op) + { + case '+': + if (!options->from(options, optarg, &argc, &argv, optind)) + { + /* a error value */ + return 255; + } + continue; + case 'v': + dbg_default_set_level(atoi(optarg)); + continue; + case 'u': + uri = optarg; + continue; + default: + *arg = optarg; + return op; + } + } +} + +/** + * Register a command + */ +void command_register(command_t command) +{ + int i; + + if (registered == MAX_COMMANDS) + { + fprintf(stderr, "unable to register command, please increase " + "MAX_COMMANDS\n"); + return; + } + + cmds[registered] = command; + /* append default options, but not to --help */ + if (!active) + { + for (i = 0; i < countof(cmds[registered].options) - 1; i++) + { + if (!cmds[registered].options[i].name) + { + break; + } + } + if (i > countof(cmds[registered].options) - 3) + { + fprintf(stderr, "command '%s' registered too many options, please " + "increase MAX_OPTIONS\n", command.cmd); + } + else + { + cmds[registered].options[i++] = (command_option_t) { + "debug", 'v', 1, "set debug level, default: 1" + }; + cmds[registered].options[i++] = (command_option_t) { + "options", '+', 1, "read command line options from file" + }; + cmds[registered].options[i++] = (command_option_t) { + "uri", 'u', 1, "service URI to connect to" + }; + } + } + registered++; +} + +/** + * Print usage text, with an optional error + */ +int command_usage(char *error, ...) +{ + va_list args; + FILE *out = stdout; + int i; + + if (error) + { + out = stderr; + fprintf(out, "Error: "); + va_start(args, error); + vfprintf(out, error, args); + va_end(args); + fprintf(out, "\n"); + } + fprintf(out, "strongSwan %s swanctl\n", VERSION); + + if (active == help_idx) + { + fprintf(out, "loaded plugins: %s\n", + lib->plugins->loaded_plugins(lib->plugins)); + } + + fprintf(out, "usage:\n"); + if (active == help_idx) + { + for (i = 0; cmds[i].cmd; i++) + { + fprintf(out, " swanctl --%-10s (-%c) %s\n", + cmds[i].cmd, cmds[i].op, cmds[i].description); + } + } + else + { + for (i = 0; cmds[active].line[i]; i++) + { + if (i == 0) + { + fprintf(out, " swanctl --%s %s\n", + cmds[active].cmd, cmds[active].line[i]); + } + else + { + fprintf(out, " %s\n", cmds[active].line[i]); + } + } + for (i = 0; cmds[active].options[i].name; i++) + { + fprintf(out, " --%-15s (-%c) %s\n", + cmds[active].options[i].name, cmds[active].options[i].op, + cmds[active].options[i].desc); + } + } + return error != NULL; +} + +/** + * Dispatch cleanup hook + */ +static void cleanup() +{ + options->destroy(options); +} + +/** + * Open vici connection, call a command + */ +static int call_command(command_t *cmd) +{ + vici_conn_t *conn; + int ret; + + conn = vici_connect(uri); + if (!conn) + { + command_usage("connecting to '%s' URI failed: %s", + uri ?: "default", strerror(errno)); + return errno; + } + ret = cmd->call(conn); + vici_disconnect(conn); + return ret; +} + +/** + * Dispatch commands. + */ +int command_dispatch(int c, char *v[]) +{ + int op, i; + + options = options_create(); + atexit(cleanup); + active = help_idx = registered; + argc = c; + argv = v; + command_register((command_t){NULL, 'h', "help", "show usage information"}); + + build_opts(); + op = getopt_long(c, v, command_optstring, command_opts, NULL); + for (i = 0; cmds[i].cmd; i++) + { + if (cmds[i].op == op) + { + active = i; + build_opts(); + if (help_idx == i) + { + return command_usage(NULL); + } + return call_command(&cmds[i]); + } + } + return command_usage(c > 1 ? "invalid operation" : NULL); +} diff --git a/src/swanctl/command.h b/src/swanctl/command.h new file mode 100644 index 000000000..699483bf1 --- /dev/null +++ b/src/swanctl/command.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2009 Martin Willi + * 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. + */ + +/** + * @defgroup command command + * @{ @ingroup swanctl + */ + +#ifndef COMMAND_H_ +#define COMMAND_H_ + +#include +#include + +/** + * Maximum number of commands (+1). + */ +#define MAX_COMMANDS 11 + +/** + * Maximum number of options in a command (+3) + */ +#define MAX_OPTIONS 32 + +/** + * Maximum number of usage summary lines (+1) + */ +#define MAX_LINES 10 + +typedef struct command_t command_t; +typedef struct command_option_t command_option_t; +typedef enum command_type_t command_type_t; + +/** + * Option specification + */ +struct command_option_t { + /** long option string of the option */ + char *name; + /** short option character of the option */ + char op; + /** expected argument to option, no/req/opt_argument */ + int arg; + /** description of the option */ + char *desc; +}; + +/** + * Command specification. + */ +struct command_t { + /** Function implementing the command */ + int (*call)(vici_conn_t *conn); + /** short option character */ + char op; + /** long option string */ + char *cmd; + /** description of the command */ + char *description; + /** usage summary of the command */ + char *line[MAX_LINES]; + /** list of options the command accepts */ + command_option_t options[MAX_OPTIONS]; +}; + +/** + * Get the next option, as with getopt. + */ +int command_getopt(char **arg); + +/** + * Register a command. + */ +void command_register(command_t command); + +/** + * Dispatch commands. + */ +int command_dispatch(int argc, char *argv[]); + +/** + * Show usage information of active command. + */ +int command_usage(char *error, ...); + +#endif /** COMMAND_H_ @}*/ diff --git a/src/swanctl/swanctl.c b/src/swanctl/swanctl.c new file mode 100644 index 000000000..7aacf839d --- /dev/null +++ b/src/swanctl/swanctl.c @@ -0,0 +1,57 @@ +/* + * 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 + * 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 + +#include + +/** + * Cleanup library atexit() + */ +static void cleanup() +{ + lib->processor->cancel(lib->processor); + library_deinit(); +} + +/** + * Library initialization and operation parsing + */ +int main(int argc, char *argv[]) +{ + atexit(cleanup); + if (!library_init(NULL, "swanctl")) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (lib->integrity && + !lib->integrity->check_file(lib->integrity, "swanctl", argv[0])) + { + fprintf(stderr, "integrity check of swanctl failed\n"); + exit(SS_RC_DAEMON_INTEGRITY); + } + if (!lib->plugins->load(lib->plugins, + lib->settings->get_str(lib->settings, "swanctl.load", PLUGINS))) + { + exit(SS_RC_INITIALIZATION_FAILED); + } + dbg_default_set_level(0); + lib->processor->set_threads(lib->processor, 4); + dbg_default_set_level(0); + + return command_dispatch(argc, argv); +} From 86910faecaff6b4b8df1ecf1a4e82d1a63da9082 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 29 Jan 2014 17:20:56 +0100 Subject: [PATCH 03/38] swanctl: Add a list-sas command to query active IKE_SAs --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/list_sas.c | 359 ++++++++++++++++++++++++++++++++ 2 files changed, 360 insertions(+) create mode 100644 src/swanctl/commands/list_sas.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index a63c8d9f7..0e8065589 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -2,6 +2,7 @@ sbin_PROGRAMS = swanctl swanctl_SOURCES = \ command.c command.h \ + commands/list_sas.c \ swanctl.c swanctl_LDADD = \ diff --git a/src/swanctl/commands/list_sas.c b/src/swanctl/commands/list_sas.c new file mode 100644 index 000000000..ae8c7cb22 --- /dev/null +++ b/src/swanctl/commands/list_sas.c @@ -0,0 +1,359 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include + +#include "command.h" + +#include + +/** + * Free hashtable with contained strings + */ +static void free_hashtable(hashtable_t *hashtable) +{ + enumerator_t *enumerator; + char *str; + + enumerator = hashtable->create_enumerator(hashtable); + while (enumerator->enumerate(enumerator, NULL, &str)) + { + free(str); + } + enumerator->destroy(enumerator); + + hashtable->destroy(hashtable); +} + +CALLBACK(sa_values, int, + hashtable_t *sa, vici_res_t *res, char *name, void *value, int len) +{ + chunk_t chunk; + char *str; + + chunk = chunk_create(value, len); + if (chunk_printable(chunk, NULL, ' ')) + { + if (asprintf(&str, "%.*s", len, value) >= 0) + { + free(sa->put(sa, name, str)); + } + } + return 0; +} + + +CALLBACK(sa_list, int, + hashtable_t *sa, vici_res_t *res, char *name, void *value, int len) +{ + chunk_t chunk; + char *str; + + chunk = chunk_create(value, len); + if (chunk_printable(chunk, NULL, ' ')) + { + str = sa->get(sa, name); + if (asprintf(&str, "%s%s%.*s", + str ?: "", str ? " " : "", len, value) >= 0) + { + free(sa->put(sa, name, str)); + } + } + return 0; +} + +CALLBACK(child_sas, int, + hashtable_t *ike, vici_res_t *res, char *name) +{ + hashtable_t *child; + int ret; + + child = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1); + ret = vici_parse_cb(res, NULL, sa_values, sa_list, child); + if (ret == 0) + { + printf(" %s: #%s, %s, %s%s, %s:", + name, child->get(child, "reqid"), + child->get(child, "state"), child->get(child, "mode"), + child->get(child, "encap") ? "-in-UDP" : "", + child->get(child, "protocol")); + + if (child->get(child, "encr-alg")) + { + printf("%s", child->get(child, "encr-alg")); + if (child->get(child, "encr-keysize")) + { + printf("-%s", child->get(child, "encr-keysize")); + } + } + if (child->get(child, "integ-alg")) + { + if (child->get(child, "encr-alg")) + { + printf("/"); + } + printf("%s", child->get(child, "integ-alg")); + if (child->get(child, "integ-keysize")) + { + printf("-%s", child->get(child, "integ-keysize")); + } + } + if (child->get(child, "prf-alg")) + { + printf("/%s", child->get(child, "prf-alg")); + } + if (child->get(child, "dh-group")) + { + printf("/%s", child->get(child, "dh-group")); + } + if (child->get(child, "esn")) + { + printf("/%s", child->get(child, "esn")); + } + printf("\n"); + + printf(" installed %s ago", child->get(child, "install-time")); + if (child->get(child, "rekey-time")) + { + printf(", rekeying in %ss", child->get(child, "rekey-time")); + } + if (child->get(child, "life-time")) + { + printf(", expires in %ss", child->get(child, "life-time")); + } + printf("\n"); + + printf(" in %s%s%s", child->get(child, "spi-in"), + child->get(child, "cpi-in") ? "/" : "", + child->get(child, "cpi-in") ?: ""); + printf(", %6s bytes, %5s packets", + child->get(child, "bytes-in"), child->get(child, "packets-in")); + if (child->get(child, "use-in")) + { + printf(", %5ss ago", child->get(child, "use-in")); + } + printf("\n"); + + printf(" out %s%s%s", child->get(child, "spi-out"), + child->get(child, "cpi-out") ? "/" : "", + child->get(child, "cpi-out") ?: ""); + printf(", %6s bytes, %5s packets", + child->get(child, "bytes-out"), child->get(child, "packets-out")); + if (child->get(child, "use-out")) + { + printf(", %5ss ago", child->get(child, "use-out")); + } + printf("\n"); + + printf(" local %s\n", child->get(child, "local-ts")); + printf(" remote %s\n", child->get(child, "remote-ts")); + } + free_hashtable(child); + return ret; +} + +CALLBACK(ike_sa, int, + hashtable_t *ike, vici_res_t *res, char *name) +{ + if (streq(name, "child-sas")) + { + printf("%s: #%s, %s, IKEv%s, %s:%s\n", + ike->get(ike, "name"), ike->get(ike, "uniqueid"), + ike->get(ike, "state"), ike->get(ike, "version"), + ike->get(ike, "initiator-spi"), ike->get(ike, "responder-spi")); + + printf(" local '%s' @ %s\n", + ike->get(ike, "local-id"), ike->get(ike, "local-host")); + printf(" remote '%s' @ %s", + ike->get(ike, "remote-id"), ike->get(ike, "remote-host")); + if (ike->get(ike, "remote-eap-id")) + { + printf(" EAP: '%s'", ike->get(ike, "remote-eap-id")); + } + if (ike->get(ike, "remote-xauth-id")) + { + printf(" XAuth: '%s'", ike->get(ike, "remote-xauth-id")); + } + printf("\n"); + + if (ike->get(ike, "encr-alg")) + { + printf(" %s", ike->get(ike, "encr-alg")); + if (ike->get(ike, "encr-keysize")) + { + printf("-%s", ike->get(ike, "encr-keysize")); + } + if (ike->get(ike, "integ-alg")) + { + printf("/%s", ike->get(ike, "integ-alg")); + } + if (ike->get(ike, "integ-keysize")) + { + printf("-%s", ike->get(ike, "integ-keysize")); + } + printf("/%s", ike->get(ike, "prf-alg")); + printf("/%s", ike->get(ike, "dh-group")); + printf("\n"); + } + + if (ike->get(ike, "established")) + { + printf(" established %s ago", ike->get(ike, "established")); + if (ike->get(ike, "rekey-time")) + { + printf(", rekeying in %ss", ike->get(ike, "rekey-time")); + } + if (ike->get(ike, "reauth-time")) + { + printf(", reauth in %ss", ike->get(ike, "reauth-time")); + } + if (ike->get(ike, "life-time")) + { + printf(", expires in %ss", ike->get(ike, "life-time")); + } + printf("\n"); + } + + if (ike->get(ike, "tasks-queued")) + { + printf(" queued: %s\n", ike->get(ike, "tasks-queued")); + } + if (ike->get(ike, "tasks-active")) + { + printf(" active: %s\n", ike->get(ike, "tasks-active")); + } + if (ike->get(ike, "tasks-passive")) + { + printf(" passive: %s\n", ike->get(ike, "tasks-passive")); + } + + return vici_parse_cb(res, child_sas, NULL, NULL, ike); + } + return 0; +} + +CALLBACK(ike_sas, int, + void *null, vici_res_t *res, char *name) +{ + hashtable_t *ike; + int ret; + + ike = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1); + ike->put(ike, "name", strdup(name)); + ret = vici_parse_cb(res, ike_sa, sa_values, sa_list, ike); + free_hashtable(ike); + return ret; +} + +CALLBACK(list_cb, void, + bool *raw, char *name, vici_res_t *res) +{ + if (*raw) + { + vici_dump(res, "list-sa event", stdout); + } + else + { + if (vici_parse_cb(res, ike_sas, NULL, NULL, NULL) != 0) + { + fprintf(stderr, "parsing SA event failed: %s\n", strerror(errno)); + } + } +} + +static int list_sas(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE, noblock = FALSE; + char *arg, *ike = NULL; + int ike_id = 0; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'i': + ike = arg; + continue; + case 'I': + ike_id = atoi(arg); + continue; + case 'n': + noblock = TRUE; + continue; + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --list-sas option"); + } + break; + } + if (vici_register(conn, "list-sa", list_cb, &raw) != 0) + { + fprintf(stderr, "registering for SAs failed: %s\n", strerror(errno)); + return errno; + } + req = vici_begin("list-sas"); + if (ike) + { + vici_add_key_valuef(req, "ike", "%s", ike); + } + if (ike_id) + { + vici_add_key_valuef(req, "ike-id", "%d", ike_id); + } + if (noblock) + { + vici_add_key_valuef(req, "noblock", "yes"); + } + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "list-sas request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "list-sas reply", stdout); + } + vici_free_res(res); + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + list_sas, 'l', "list-sas", "list currently active IKE_SAs", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"ike", 'i', 1, "filter IKE_SAs by name"}, + {"ike-id", 'I', 1, "filter IKE_SAs by unique identifier"}, + {"noblock", 'n', 0, "don't wait for IKE_SAs in use"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From cb1c409b8403ed9615b79b8657f5246647cd2191 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 10 Feb 2014 17:11:42 +0100 Subject: [PATCH 04/38] swanctl: Add a subcommand to initiate connections by name --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/initiate.c | 128 ++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 src/swanctl/commands/initiate.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 0e8065589..968f795cd 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -2,6 +2,7 @@ sbin_PROGRAMS = swanctl swanctl_SOURCES = \ command.c command.h \ + commands/initiate.c \ commands/list_sas.c \ swanctl.c diff --git a/src/swanctl/commands/initiate.c b/src/swanctl/commands/initiate.c new file mode 100644 index 000000000..a4a83737c --- /dev/null +++ b/src/swanctl/commands/initiate.c @@ -0,0 +1,128 @@ +/* + * 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 + * 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 + + +CALLBACK(log_cb, void, + bool *raw, char *name, vici_res_t *msg) +{ + if (*raw) + { + vici_dump(msg, "log", stdout); + } + else + { + printf("[%s] %s\n", + vici_find_str(msg, " ", "group"), + vici_find_str(msg, "", "msg")); + } +} + +static int initiate(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE; + char *arg, *child = NULL; + int ret = 0, timeout = 0, level = 1; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case 'c': + child = arg; + continue; + case 't': + timeout = atoi(arg); + continue; + case 'l': + level = atoi(arg); + continue; + case EOF: + break; + default: + return command_usage("invalid --initiate option"); + } + break; + } + + if (vici_register(conn, "control-log", log_cb, &raw) != 0) + { + fprintf(stderr, "registering for log failed: %s\n", strerror(errno)); + return errno; + } + req = vici_begin("initiate"); + if (child) + { + vici_add_key_valuef(req, "child", "%s", child); + } + if (timeout) + { + vici_add_key_valuef(req, "timeout", "%d", timeout * 1000); + } + vici_add_key_valuef(req, "loglevel", "%d", level); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "initiate request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "initiate reply", stdout); + } + else + { + if (streq(vici_find_str(res, "no", "success"), "yes")) + { + printf("initiate completed successfully\n"); + } + else + { + fprintf(stderr, "initiate 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) { + initiate, 'i', "initiate", "initiate a connection", + {"--child [--timeout ] [--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"child", 'c', 1, "initate a CHILD_SA configuration"}, + {"timeout", 't', 1, "timeout in seconds before detaching"}, + {"raw", 'r', 0, "dump raw response message"}, + {"loglevel", 'l', 1, "verbosity of redirected log"}, + } + }); +} From 3dc377b37f69bb1dd1c1319532fde308a3f3bee7 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 11 Feb 2014 17:14:51 +0100 Subject: [PATCH 05/38] swanctl: Add a terminate command --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/terminate.c | 153 +++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/swanctl/commands/terminate.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 968f795cd..30bff43e4 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -3,6 +3,7 @@ sbin_PROGRAMS = swanctl swanctl_SOURCES = \ command.c command.h \ commands/initiate.c \ + commands/terminate.c \ commands/list_sas.c \ swanctl.c diff --git a/src/swanctl/commands/terminate.c b/src/swanctl/commands/terminate.c new file mode 100644 index 000000000..b0e2c6671 --- /dev/null +++ b/src/swanctl/commands/terminate.c @@ -0,0 +1,153 @@ +/* + * 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 + * 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 + + +CALLBACK(log_cb, void, + bool *raw, char *name, vici_res_t *msg) +{ + if (*raw) + { + vici_dump(msg, "log", stdout); + } + else + { + printf("[%s] %s\n", + vici_find_str(msg, " ", "group"), + vici_find_str(msg, "", "msg")); + } +} + +static int terminate(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE; + char *arg, *child = NULL, *ike = NULL; + int ret = 0, timeout = 0, level = 1, child_id = 0, ike_id = 0; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + 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 't': + timeout = atoi(arg); + continue; + case 'l': + level = atoi(arg); + continue; + case EOF: + break; + default: + return command_usage("invalid --terminate option"); + } + break; + } + + if (vici_register(conn, "control-log", log_cb, &raw) != 0) + { + fprintf(stderr, "registering for log failed: %s\n", strerror(errno)); + return errno; + } + req = vici_begin("terminate"); + 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); + } + if (timeout) + { + vici_add_key_valuef(req, "timeout", "%d", timeout * 1000); + } + vici_add_key_valuef(req, "loglevel", "%d", level); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "terminate request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "terminate reply", stdout); + } + else + { + if (streq(vici_find_str(res, "no", "success"), "yes")) + { + printf("terminate completed successfully\n"); + } + else + { + fprintf(stderr, "terminate 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) { + terminate, 't', "terminate", "terminate a connection", + {"--child | --ike | --ike-id ", + "[--timeout ] [--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"child", 'c', 1, "terminate by CHILD_SA name"}, + {"ike", 'i', 1, "terminate by IKE_SA name"}, + {"child-id", 'C', 1, "terminate by CHILD_SA reqid"}, + {"ike-id", 'I', 1, "terminate by IKE_SA unique identifier"}, + {"timeout", 't', 1, "timeout in seconds before detaching"}, + {"raw", 'r', 0, "dump raw response message"}, + {"loglevel", 'l', 1, "verbosity of redirected log"}, + } + }); +} From 073be3cad4ca901db57292cff32e939bd21cd0fb Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 11 Feb 2014 17:41:56 +0100 Subject: [PATCH 06/38] swanctl: Add a version command to query daemon and OS info --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/version.c | 81 ++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 src/swanctl/commands/version.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 30bff43e4..582c00b57 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/version.c \ commands/list_sas.c \ swanctl.c diff --git a/src/swanctl/commands/version.c b/src/swanctl/commands/version.c new file mode 100644 index 000000000..36b7a6db4 --- /dev/null +++ b/src/swanctl/commands/version.c @@ -0,0 +1,81 @@ +/* + * 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 + * 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 version(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + char *arg; + bool raw = FALSE; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --terminate option"); + } + break; + } + + req = vici_begin("version"); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "version request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "version reply", stdout); + } + else + { + printf("strongSwan %s %s (%s, %s, %s)\n", + vici_find_str(res, "", "version"), + vici_find_str(res, "", "daemon"), + vici_find_str(res, "", "sysname"), + vici_find_str(res, "", "release"), + vici_find_str(res, "", "machine")); + } + vici_free_res(res); + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + version, 'v', "version", "show daemon version information", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From 90ae636ccb0b2b34569cf0090a57eb9a40707c52 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 13 Feb 2014 15:23:16 +0100 Subject: [PATCH 07/38] swanctl: Implement install/uninstall commands to manage shunt/trap policies --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/install.c | 120 +++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/swanctl/commands/install.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 582c00b57..d269c2218 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/install.c \ commands/version.c \ commands/list_sas.c \ swanctl.c diff --git a/src/swanctl/commands/install.c b/src/swanctl/commands/install.c new file mode 100644 index 000000000..a0cef58d8 --- /dev/null +++ b/src/swanctl/commands/install.c @@ -0,0 +1,120 @@ +/* + * 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 + * 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 manage_policy(vici_conn_t *conn, char *label) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE; + char *arg, *child = NULL; + int ret; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case 'c': + child = arg; + continue; + case EOF: + break; + default: + return command_usage("invalid --%s option", label); + } + break; + } + req = vici_begin(label); + if (child) + { + vici_add_key_valuef(req, "child", "%s", child); + } + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "%s request failed: %s\n", label, strerror(errno)); + return errno; + } + if (raw) + { + puts(label); + vici_dump(res, " reply", stdout); + } + else + { + if (streq(vici_find_str(res, "no", "success"), "yes")) + { + printf("%s completed successfully\n", label); + } + else + { + fprintf(stderr, "%s failed: %s\n", + label, vici_find_str(res, "", "errmsg")); + ret = 1; + } + } + vici_free_res(res); + return ret; +} + +static int uninstall(vici_conn_t *conn) +{ + return manage_policy(conn, "uninstall"); +} + +static int install(vici_conn_t *conn) +{ + return manage_policy(conn, "install"); +} + +/** + * Register the uninstall command. + */ +static void __attribute__ ((constructor))reg_uninstall() +{ + command_register((command_t) { + uninstall, 'u', "uninstall", "uninstall a trap or shunt policy", + {"--child [--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"child", 'c', 1, "CHILD_SA configuration to uninstall"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} + +/** + * Register install the command. + */ +static void __attribute__ ((constructor))reg_install() +{ + command_register((command_t) { + install, 'p', "install", "install a trap or shunt policy", + {"--child [--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"child", 'c', 1, "CHILD_SA configuration to install"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From 283b0b9e92c0c27976d1bacc97ad82d5148477da Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 13 Feb 2014 16:13:09 +0100 Subject: [PATCH 08/38] swanctl: Implement a list-pols command to query trap/shunt policies --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/list_pols.c | 204 +++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/swanctl/commands/list_pols.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index d269c2218..45b8dad13 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -7,6 +7,7 @@ swanctl_SOURCES = \ commands/install.c \ commands/version.c \ commands/list_sas.c \ + commands/list_pols.c \ swanctl.c swanctl_LDADD = \ diff --git a/src/swanctl/commands/list_pols.c b/src/swanctl/commands/list_pols.c new file mode 100644 index 000000000..a65753836 --- /dev/null +++ b/src/swanctl/commands/list_pols.c @@ -0,0 +1,204 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include + +#include "command.h" + +#include + +/** + * Free hashtable with contained strings + */ +static void free_hashtable(hashtable_t *hashtable) +{ + enumerator_t *enumerator; + char *str; + + enumerator = hashtable->create_enumerator(hashtable); + while (enumerator->enumerate(enumerator, NULL, &str)) + { + free(str); + } + enumerator->destroy(enumerator); + + hashtable->destroy(hashtable); +} + +CALLBACK(policy_values, int, + hashtable_t *pol, vici_res_t *res, char *name, void *value, int len) +{ + chunk_t chunk; + char *str; + + chunk = chunk_create(value, len); + if (chunk_printable(chunk, NULL, ' ')) + { + if (asprintf(&str, "%.*s", len, value) >= 0) + { + free(pol->put(pol, name, str)); + } + } + return 0; +} + +CALLBACK(policy_list, int, + hashtable_t *pol, vici_res_t *res, char *name, void *value, int len) +{ + chunk_t chunk; + char *str; + + chunk = chunk_create(value, len); + if (chunk_printable(chunk, NULL, ' ')) + { + str = pol->get(pol, name); + if (asprintf(&str, "%s%s%.*s", + str ?: "", str ? " " : "", len, value) >= 0) + { + free(pol->put(pol, name, str)); + } + } + return 0; +} + +CALLBACK(policies, int, + void *null, vici_res_t *res, char *name) +{ + hashtable_t *pol; + int ret; + + pol = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1); + ret = vici_parse_cb(res, NULL, policy_values, policy_list, pol); + + printf("%s, %s\n", name, pol->get(pol, "mode")); + printf(" local: %s\n", pol->get(pol, "local-ts")); + printf(" remote: %s\n", pol->get(pol, "remote-ts")); + + free_hashtable(pol); + return ret; +} + +CALLBACK(list_cb, void, + bool *raw, char *name, vici_res_t *res) +{ + if (*raw) + { + vici_dump(res, "list-policy event", stdout); + } + else + { + if (vici_parse_cb(res, policies, NULL, NULL, NULL) != 0) + { + fprintf(stderr, "parsing policy event failed: %s\n", strerror(errno)); + } + } +} + +static int list_pols(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE, trap = FALSE, drop = FALSE, pass = FALSE; + char *arg, *child = NULL; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'c': + child = arg; + continue; + case 't': + trap = TRUE; + continue; + case 'd': + drop = TRUE; + continue; + case 'p': + pass = TRUE; + continue; + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --list-pols option"); + } + break; + } + if (!trap && !drop && !pass) + { + trap = drop = pass = TRUE; + } + if (vici_register(conn, "list-policy", list_cb, &raw) != 0) + { + fprintf(stderr, "registering for policies failed: %s\n", + strerror(errno)); + return errno; + } + req = vici_begin("list-policies"); + if (child) + { + vici_add_key_valuef(req, "child", "%s", child); + } + if (trap) + { + vici_add_key_valuef(req, "trap", "yes"); + } + if (drop) + { + vici_add_key_valuef(req, "drop", "yes"); + } + if (pass) + { + vici_add_key_valuef(req, "pass", "yes"); + } + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "list-policies request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "list-policies reply", stdout); + } + vici_free_res(res); + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + list_pols, 'P', "list-pols", "list currently installed policies", + {"[--child ] [--trap] [--drop] [--pass] [--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"child", 'c', 1, "filter policies by CHILD_SA config name"}, + {"trap", 't', 0, "list trap policies"}, + {"drop", 'd', 0, "list drop policies"}, + {"pass", 'p', 0, "list bypass policies"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From ee599d14adc39fd69d516792fbba451b8674f731 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 17 Feb 2014 18:30:32 +0100 Subject: [PATCH 09/38] swanctl: Implement a load-conn command to load connections from a file --- src/swanctl/Makefile.am | 4 + src/swanctl/commands/load_conns.c | 225 ++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 src/swanctl/commands/load_conns.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 45b8dad13..58995cdda 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -1,5 +1,7 @@ sbin_PROGRAMS = swanctl +conffile = `dirname $(strongswan_conf)`/strongswan.d/swanctl.conf + swanctl_SOURCES = \ command.c command.h \ commands/initiate.c \ @@ -8,6 +10,7 @@ swanctl_SOURCES = \ commands/version.c \ commands/list_sas.c \ commands/list_pols.c \ + commands/load_conns.c \ swanctl.c swanctl_LDADD = \ @@ -19,4 +22,5 @@ swanctl.o : $(top_builddir)/config.status AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libcharon/plugins/vici \ + -DCONF_FILE=\""${conffile}\"" \ -DPLUGINS=\""${s_plugins}\"" diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c new file mode 100644 index 000000000..b6842e00d --- /dev/null +++ b/src/swanctl/commands/load_conns.c @@ -0,0 +1,225 @@ +/* + * 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 + * 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 + +/** + * Check if we should handle a key as a list of comma separated values + */ +static bool is_list_key(char *key) +{ + char *keys[] = { + "local_addrs", + "remote_addrs", + "proposals", + "esp_proposals", + "ah_proposals", + "local_ts", + "remote_ts", + "vips", + }; + int i; + + for (i = 0; i < countof(keys); i++) + { + if (strcaseeq(keys[i], key)) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Add a vici list from a comma separated string value + */ +static void add_list_key(vici_req_t *req, char *key, char *value) +{ + enumerator_t *enumerator; + char *token; + + vici_begin_list(req, key); + enumerator = enumerator_create_token(value, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + vici_add_list_itemf(req, "%s", token); + } + enumerator->destroy(enumerator); + vici_end_list(req); +} + +/** + * Translate setting key/values from a section into vici key-values/lists + */ +static void add_key_values(vici_req_t *req, settings_t *cfg, char *section) +{ + enumerator_t *enumerator; + char *key, *value; + + enumerator = cfg->create_key_value_enumerator(cfg, section); + while (enumerator->enumerate(enumerator, &key, &value)) + { + if (is_list_key(key)) + { + add_list_key(req, key, value); + } + else + { + vici_add_key_valuef(req, key, "%s", value); + } + } + enumerator->destroy(enumerator); +} + +/** + * Translate a settings section to a vici section + */ +static void add_sections(vici_req_t *req, settings_t *cfg, char *section) +{ + enumerator_t *enumerator; + char *name, buf[256]; + + enumerator = cfg->create_section_enumerator(cfg, section); + while (enumerator->enumerate(enumerator, &name)) + { + vici_begin_section(req, name); + snprintf(buf, sizeof(buf), "%s.%s", section, name); + add_key_values(req, cfg, buf); + add_sections(req, cfg, buf); + vici_end_section(req); + } + enumerator->destroy(enumerator); +} + +/** + * Load an IKE_SA config with CHILD_SA configs from a section + */ +static bool load_conn(vici_conn_t *conn, settings_t *cfg, + char *section, bool raw) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + char buf[128]; + + snprintf(buf, sizeof(buf), "%s.%s", "connections", section); + + req = vici_begin("load-conn"); + + vici_begin_section(req, section); + add_key_values(req, cfg, buf); + add_sections(req, cfg, buf); + vici_end_section(req); + + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "load-conn request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "load-conn reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "loading connection '%s' failed: %s\n", + section, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + +static int load_conns(vici_conn_t *conn) +{ + bool raw = FALSE; + u_int found = 0, loaded = 0; + char *arg, *section; + enumerator_t *enumerator; + settings_t *cfg; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --load-conns option"); + } + break; + } + + cfg = settings_create(CONF_FILE); + if (!cfg) + { + fprintf(stderr, "parsing '%s' failed\n", CONF_FILE); + return EINVAL; + } + + enumerator = cfg->create_section_enumerator(cfg, "connections"); + while (enumerator->enumerate(enumerator, §ion)) + { + found++; + if (load_conn(conn, cfg, section, raw)) + { + loaded++; + } + } + enumerator->destroy(enumerator); + + cfg->destroy(cfg); + + if (raw) + { + return 0; + } + if (found == 0) + { + printf("no connections found\n"); + return 0; + } + if (loaded == found) + { + printf("successfully loaded %u connections\n", loaded); + return 0; + } + fprintf(stderr, "loaded %u of %u connections, %u failed to load\n", + loaded, found, found - loaded); + return EINVAL; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + load_conns, 'c', "load-conns", "(re-)load connection configuration", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From 991c9b5e77b9489e929f7f608a52382fe45d1923 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 18 Feb 2014 15:33:22 +0100 Subject: [PATCH 10/38] swanctl: After loading connections, unload those that are not in config anymore --- src/swanctl/commands/load_conns.c | 121 ++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 7 deletions(-) diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index b6842e00d..7fcf87f87 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -13,10 +13,12 @@ * for more details. */ -#include "command.h" - +#define _GNU_SOURCE +#include #include +#include "command.h" + /** * Check if we should handle a key as a list of comma separated values */ @@ -145,12 +147,102 @@ static bool load_conn(vici_conn_t *conn, settings_t *cfg, return ret; } +CALLBACK(list_conn, int, + linked_list_t *list, vici_res_t *res, char *name, void *value, int len) +{ + if (streq(name, "conns")) + { + char *str; + + if (asprintf(&str, "%.*s", len, value) != -1) + { + list->insert_last(list, str); + } + } + return 0; +} + +/** + * Create a list of currently loaded connections + */ +static linked_list_t* list_conns(vici_conn_t *conn, bool raw) +{ + linked_list_t *list; + vici_res_t *res; + + list = linked_list_create(); + + res = vici_submit(vici_begin("get-conns"), conn); + if (res) + { + if (raw) + { + vici_dump(res, "get-conns reply", stdout); + } + vici_parse_cb(res, NULL, NULL, list_conn, list); + vici_free_res(res); + } + return list; +} + +/** + * Remove and free a string from a list + */ +static void remove_from_list(linked_list_t *list, char *str) +{ + enumerator_t *enumerator; + char *current; + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current, str)) + { + list->remove_at(list, enumerator); + free(current); + } + } + enumerator->destroy(enumerator); +} + +/** + * Unload a connection by name + */ +static bool unload_conn(vici_conn_t *conn, char *name, bool raw) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + + req = vici_begin("unload-conn"); + vici_add_key_valuef(req, "name", "%s", name); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "unload-conn request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "unload-conn reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "unloading connection '%s' failed: %s\n", + name, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + static int load_conns(vici_conn_t *conn) { bool raw = FALSE; - u_int found = 0, loaded = 0; + u_int found = 0, loaded = 0, unloaded = 0; char *arg, *section; enumerator_t *enumerator; + linked_list_t *conns; settings_t *cfg; while (TRUE) @@ -177,9 +269,12 @@ static int load_conns(vici_conn_t *conn) return EINVAL; } + conns = list_conns(conn, raw); + enumerator = cfg->create_section_enumerator(cfg, "connections"); while (enumerator->enumerate(enumerator, §ion)) { + remove_from_list(conns, section); found++; if (load_conn(conn, cfg, section, raw)) { @@ -190,22 +285,34 @@ static int load_conns(vici_conn_t *conn) cfg->destroy(cfg); + /* unload all connection in daemon, but not in file */ + while (conns->remove_first(conns, (void**)§ion) == SUCCESS) + { + if (unload_conn(conn, section, raw)) + { + unloaded++; + } + free(section); + } + conns->destroy(conns); + if (raw) { return 0; } if (found == 0) { - printf("no connections found\n"); + printf("no connections found, %u unloaded\n", unloaded); return 0; } if (loaded == found) { - printf("successfully loaded %u connections\n", loaded); + printf("successfully loaded %u connections, %u unloaded\n", + loaded, unloaded); return 0; } - fprintf(stderr, "loaded %u of %u connections, %u failed to load\n", - loaded, found, found - loaded); + fprintf(stderr, "loaded %u of %u connections, %u failed to load, " + "%u unloaded\n", loaded, found, found - loaded, unloaded); return EINVAL; } From 7c8a907895cffff6e2e8734e25a9cee78b5df77e Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 11:09:59 +0100 Subject: [PATCH 11/38] swanctl: Use a ./configure-able swanctl base directory --- configure.ac | 1 + src/swanctl/Makefile.am | 6 ++---- src/swanctl/commands/load_conns.c | 5 +++-- src/swanctl/swanctl.h | 29 +++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/swanctl/swanctl.h diff --git a/configure.ac b/configure.ac index 336057a92..6c056cff7 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,7 @@ ARG_WITH_SUBST([ipseclibdir], [${libdir%/}/ipsec], [set installation path ARG_WITH_SUBST([plugindir], [${ipseclibdir%/}/plugins], [set the installation path of plugins]) ARG_WITH_SUBST([imcvdir], [${ipseclibdir%/}/imcvs], [set the installation path of IMC and IMV dynamic librariers]) ARG_WITH_SUBST([nm-ca-dir], [/usr/share/ca-certificates], [directory the NM backend uses to look up trusted root certificates]) +ARG_WITH_SUBST([swanctldir], [${sysconfdir}/swanctl], [base directory for swanctl configuration files and credentials]) ARG_WITH_SUBST([linux-headers], [\${top_srcdir}/src/include], [set directory of linux header files to use]) ARG_WITH_SUBST([routing-table], [220], [set routing table to use for IPsec routes]) ARG_WITH_SUBST([routing-table-prio], [220], [set priority for IPsec routing table]) diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 58995cdda..c6b71c8b2 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -1,7 +1,5 @@ sbin_PROGRAMS = swanctl -conffile = `dirname $(strongswan_conf)`/strongswan.d/swanctl.conf - swanctl_SOURCES = \ command.c command.h \ commands/initiate.c \ @@ -11,7 +9,7 @@ swanctl_SOURCES = \ commands/list_sas.c \ commands/list_pols.c \ commands/load_conns.c \ - swanctl.c + swanctl.c swanctl.h swanctl_LDADD = \ $(top_builddir)/src/libcharon/plugins/vici/libvici.la \ @@ -22,5 +20,5 @@ swanctl.o : $(top_builddir)/config.status AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libcharon/plugins/vici \ - -DCONF_FILE=\""${conffile}\"" \ + -DSWANCTLDIR=\""${swanctldir}\"" \ -DPLUGINS=\""${s_plugins}\"" diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 7fcf87f87..2c9884dc0 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -18,6 +18,7 @@ #include #include "command.h" +#include "swanctl.h" /** * Check if we should handle a key as a list of comma separated values @@ -262,10 +263,10 @@ static int load_conns(vici_conn_t *conn) break; } - cfg = settings_create(CONF_FILE); + cfg = settings_create(SWANCTL_CONF); if (!cfg) { - fprintf(stderr, "parsing '%s' failed\n", CONF_FILE); + fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF); return EINVAL; } diff --git a/src/swanctl/swanctl.h b/src/swanctl/swanctl.h new file mode 100644 index 000000000..8497f230b --- /dev/null +++ b/src/swanctl/swanctl.h @@ -0,0 +1,29 @@ +/* + * 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 + * 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. + */ + +/** + * @defgroup swanctl swanctl + * @{ @ingroup swanctl + */ + +#ifndef SWANCTL_H_ +#define SWANCTL_H_ + +/** + * Configuration file for connections, etc. + */ +#define SWANCTL_CONF SWANCTLDIR "/swanctl.conf" + +#endif /** SWANCTL_H_ @}*/ From 2c1511dbf8e8372ea20aa9e90667d43db2a3fb11 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 11:54:42 +0100 Subject: [PATCH 12/38] swanctl: Add a command to (re-)load credentials --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/load_creds.c | 170 ++++++++++++++++++++++++++++++ src/swanctl/swanctl.h | 26 +++++ 3 files changed, 197 insertions(+) create mode 100644 src/swanctl/commands/load_creds.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index c6b71c8b2..dbe1354c6 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -9,6 +9,7 @@ swanctl_SOURCES = \ commands/list_sas.c \ commands/list_pols.c \ commands/load_conns.c \ + commands/load_creds.c \ swanctl.c swanctl.h swanctl_LDADD = \ diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c new file mode 100644 index 000000000..83f29238e --- /dev/null +++ b/src/swanctl/commands/load_creds.c @@ -0,0 +1,170 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "command.h" +#include "swanctl.h" + +/** + * Load a single certificate over vici + */ +static bool load_cert(vici_conn_t *conn, bool raw, char *dir, + char *type, chunk_t data) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + + req = vici_begin("load-cert"); + + vici_add_key_valuef(req, "type", "%s", type); + vici_add_key_value(req, "data", data.ptr, data.len); + + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "load-cert request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "load-cert reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "loading '%s' failed: %s\n", + dir, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + +/** + * Load certficiates from a directory + */ +static void load_certs(vici_conn_t *conn, bool raw, char *type, char *dir) +{ + enumerator_t *enumerator; + struct stat st; + chunk_t *map; + char *path; + + enumerator = enumerator_create_directory(dir); + if (enumerator) + { + while (enumerator->enumerate(enumerator, NULL, &path, &st)) + { + if (S_ISREG(st.st_mode)) + { + map = chunk_map(path, FALSE); + if (map) + { + load_cert(conn, raw, path, type, *map); + chunk_unmap(map); + } + else + { + fprintf(stderr, "mapping '%s' failed: %s, skipped\n", + path, strerror(errno)); + } + } + } + enumerator->destroy(enumerator); + } +} + +/** + * Clear all currently loaded credentials + */ +static bool clear_creds(vici_conn_t *conn, bool raw) +{ + vici_res_t *res; + + res = vici_submit(vici_begin("clear-creds"), conn); + if (!res) + { + fprintf(stderr, "clear-creds request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "clear-creds reply", stdout); + } + vici_free_res(res); + return TRUE; +} + +static int load_creds(vici_conn_t *conn) +{ + bool raw = FALSE, clear = FALSE; + char *arg; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'c': + clear = TRUE; + continue; + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --load-creds option"); + } + break; + } + + if (clear) + { + if (!clear_creds(conn, raw)) + { + return ECONNREFUSED; + } + } + + load_certs(conn, raw, "x509", SWANCTL_X509DIR); + load_certs(conn, raw, "x509ca", SWANCTL_X509CADIR); + load_certs(conn, raw, "x509aa", SWANCTL_X509AADIR); + load_certs(conn, raw, "x509crl", SWANCTL_X509CRLDIR); + load_certs(conn, raw, "x509ac", SWANCTL_X509ACDIR); + + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + load_creds, 's', "load-creds", "(re-)load credentials", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"clear", 'c', 0, "clear previously loaded credentials"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} diff --git a/src/swanctl/swanctl.h b/src/swanctl/swanctl.h index 8497f230b..f8469a79a 100644 --- a/src/swanctl/swanctl.h +++ b/src/swanctl/swanctl.h @@ -26,4 +26,30 @@ */ #define SWANCTL_CONF SWANCTLDIR "/swanctl.conf" +/** + * Directory for X.509 end entity certs + */ +#define SWANCTL_X509DIR SWANCTLDIR "/x509" + +/** + * Directory for X.509 CA certs + */ +#define SWANCTL_X509CADIR SWANCTLDIR "/x509ca" + +/** + * Directory for X.509 Attribute Authority certs + */ +#define SWANCTL_X509AADIR SWANCTLDIR "/x509aa" + +/** + * Directory for X.509 CRLs + */ +#define SWANCTL_X509CRLDIR SWANCTLDIR "/x509crl" + +/** + * Directory for X.509 Attribute certificates + */ +#define SWANCTL_X509ACDIR SWANCTLDIR "/x509ac" + + #endif /** SWANCTL_H_ @}*/ From d622e6da0ff58b3ad0b33cfff4a555b213da571a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 14:14:15 +0100 Subject: [PATCH 13/38] swanctl: Load different private keys with load-creds --- src/swanctl/commands/load_creds.c | 184 +++++++++++++++++++++++++++++- src/swanctl/swanctl.h | 14 +++ 2 files changed, 197 insertions(+), 1 deletion(-) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 83f29238e..dfd919ada 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -16,11 +16,14 @@ #define _GNU_SOURCE #include #include +#include #include #include "command.h" #include "swanctl.h" +#include + /** * Load a single certificate over vici */ @@ -90,6 +93,177 @@ static void load_certs(vici_conn_t *conn, bool raw, char *type, char *dir) } } +/** + * Load a single private key over vici + */ +static bool load_key(vici_conn_t *conn, bool raw, char *dir, + char *type, chunk_t data) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + + req = vici_begin("load-key"); + + vici_add_key_valuef(req, "type", "%s", type); + vici_add_key_value(req, "data", data.ptr, data.len); + + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "load-key request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "load-key reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "loading '%s' failed: %s\n", + dir, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + +/** + * Callback function to prompt for private key passwords + */ +CALLBACK(password_cb, shared_key_t*, + char *prompt, shared_key_type_t type, + identification_t *me, identification_t *other, + id_match_t *match_me, id_match_t *match_other) +{ + char *pwd; + + if (type != SHARED_PRIVATE_KEY_PASS) + { + return NULL; + } + pwd = getpass(prompt); + if (!pwd || strlen(pwd) == 0) + { + return NULL; + } + if (match_me) + { + *match_me = ID_MATCH_PERFECT; + } + if (match_other) + { + *match_other = ID_MATCH_PERFECT; + } + return shared_key_create(type, chunk_clone(chunk_from_str(pwd))); +} + +/** + * Try to parse a potentially encrypted private key + */ +static private_key_t* decrypt_key(char *name, char *type, chunk_t encoding) +{ + key_type_t kt = KEY_ANY; + private_key_t *private; + callback_cred_t *cb; + char buf[128]; + + if (streq(type, "rsa")) + { + kt = KEY_RSA; + } + else if (streq(type, "ecdsa")) + { + kt = KEY_ECDSA; + } + + snprintf(buf, sizeof(buf), "Password for '%s': ", name); + + cb = callback_cred_create_shared(password_cb, buf); + lib->credmgr->add_set(lib->credmgr, &cb->set); + + private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, kt, + BUILD_BLOB_PEM, encoding, BUILD_END); + + lib->credmgr->remove_set(lib->credmgr, &cb->set); + cb->destroy(cb); + + return private; +} + +/** + * Try to decrypt and load a private key + */ +static bool load_encrypted_key(vici_conn_t *conn, bool raw, + char *rel, char *path, char *type, chunk_t data) +{ + private_key_t *private; + bool loaded = FALSE; + chunk_t encoding; + + private = decrypt_key(rel, type, data); + if (private) + { + if (private->get_encoding(private, PRIVKEY_ASN1_DER, + &encoding)) + { + switch (private->get_type(private)) + { + case KEY_RSA: + loaded = load_key(conn, raw, path, "rsa", encoding); + break; + case KEY_ECDSA: + loaded = load_key(conn, raw, path, "ecdsa", encoding); + break; + default: + break; + } + chunk_clear(&encoding); + } + private->destroy(private); + } + return loaded; +} + +/** + * Load private keys from a directory + */ +static void load_keys(vici_conn_t *conn, bool raw, bool noprompt, + char *type, char *dir) +{ + enumerator_t *enumerator; + struct stat st; + chunk_t *map; + char *path, *rel; + + enumerator = enumerator_create_directory(dir); + if (enumerator) + { + while (enumerator->enumerate(enumerator, &rel, &path, &st)) + { + if (S_ISREG(st.st_mode)) + { + map = chunk_map(path, FALSE); + if (map) + { + if (noprompt || + !load_encrypted_key(conn, raw, rel, path, type, *map)) + { + load_key(conn, raw, path, type, *map); + } + chunk_unmap(map); + } + else + { + fprintf(stderr, "mapping '%s' failed: %s, skipped\n", + path, strerror(errno)); + } + } + } + enumerator->destroy(enumerator); + } +} + /** * Clear all currently loaded credentials */ @@ -113,7 +287,7 @@ static bool clear_creds(vici_conn_t *conn, bool raw) static int load_creds(vici_conn_t *conn) { - bool raw = FALSE, clear = FALSE; + bool raw = FALSE, clear = FALSE, noprompt = FALSE; char *arg; while (TRUE) @@ -125,6 +299,9 @@ static int load_creds(vici_conn_t *conn) case 'c': clear = TRUE; continue; + case 'n': + noprompt = TRUE; + continue; case 'r': raw = TRUE; continue; @@ -150,6 +327,10 @@ static int load_creds(vici_conn_t *conn) load_certs(conn, raw, "x509crl", SWANCTL_X509CRLDIR); load_certs(conn, raw, "x509ac", SWANCTL_X509ACDIR); + load_keys(conn, raw, noprompt, "rsa", SWANCTL_RSADIR); + load_keys(conn, raw, noprompt, "ecdsa", SWANCTL_ECDSADIR); + load_keys(conn, raw, noprompt, "any", SWANCTL_PKCS8DIR); + return 0; } @@ -164,6 +345,7 @@ static void __attribute__ ((constructor))reg() { {"help", 'h', 0, "show usage information"}, {"clear", 'c', 0, "clear previously loaded credentials"}, + {"noprompt", 'n', 0, "do not prompt for passwords"}, {"raw", 'r', 0, "dump raw response message"}, } }); diff --git a/src/swanctl/swanctl.h b/src/swanctl/swanctl.h index f8469a79a..e1c7d91f5 100644 --- a/src/swanctl/swanctl.h +++ b/src/swanctl/swanctl.h @@ -51,5 +51,19 @@ */ #define SWANCTL_X509ACDIR SWANCTLDIR "/x509ac" +/** + * Directory for RSA private keys + */ +#define SWANCTL_RSADIR SWANCTLDIR "/rsa" + +/** + * Directory for ECDSA private keys + */ +#define SWANCTL_ECDSADIR SWANCTLDIR "/ecdsa" + +/** + * Directory for PKCS#8 encoded private keys + */ +#define SWANCTL_PKCS8DIR SWANCTLDIR "/pkcs8" #endif /** SWANCTL_H_ @}*/ From 818acc8638aa4b23e93fc4d4209523f492b0db99 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 15:08:39 +0100 Subject: [PATCH 14/38] swanctl: Load shared secrets from the swanctl.conf secrets section --- src/swanctl/commands/load_creds.c | 97 ++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index dfd919ada..dcfc6fe1f 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -264,6 +264,83 @@ static void load_keys(vici_conn_t *conn, bool raw, bool noprompt, } } +/** + * Load a single secret for ids over VICI + */ +static bool load_secret(vici_conn_t *conn, char *type, char *owners, + char *value, bool raw) +{ + enumerator_t *enumerator; + vici_req_t *req; + vici_res_t *res; + chunk_t data; + bool ret = TRUE; + + req = vici_begin("load-shared"); + + vici_add_key_valuef(req, "type", "%s", type); + vici_begin_list(req, "owners"); + enumerator = enumerator_create_token(owners, " ", " "); + while (enumerator->enumerate(enumerator, &owners)) + { + vici_add_list_itemf(req, "%s", owners); + } + enumerator->destroy(enumerator); + vici_end_list(req); + + if (strcasepfx(value, "0x")) + { + data = chunk_from_hex(chunk_from_str(value + 2), NULL); + } + else if (strcasepfx(value, "0s")) + { + data = chunk_from_base64(chunk_from_str(value + 2), NULL); + } + else + { + data = chunk_clone(chunk_from_str(value)); + } + vici_add_key_value(req, "data", data.ptr, data.len); + chunk_clear(&data); + + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "load-shared request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "load-shared reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "loading shared secret failed: %s\n", + vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + +/** + * Load secrets from settings section + */ +static void load_secrets(vici_conn_t *conn, settings_t *cfg, + char *section, bool raw) +{ + enumerator_t *enumerator; + char buf[64], *key, *value; + + snprintf(buf, sizeof(buf), "secrets.%s", section); + enumerator = cfg->create_key_value_enumerator(cfg, buf); + while (enumerator->enumerate(enumerator, &key, &value)) + { + load_secret(conn, section, key, value, raw); + } + enumerator->destroy(enumerator); +} + /** * Clear all currently loaded credentials */ @@ -288,7 +365,9 @@ static bool clear_creds(vici_conn_t *conn, bool raw) static int load_creds(vici_conn_t *conn) { bool raw = FALSE, clear = FALSE, noprompt = FALSE; - char *arg; + enumerator_t *enumerator; + settings_t *cfg; + char *arg, *section; while (TRUE) { @@ -331,6 +410,22 @@ static int load_creds(vici_conn_t *conn) load_keys(conn, raw, noprompt, "ecdsa", SWANCTL_ECDSADIR); load_keys(conn, raw, noprompt, "any", SWANCTL_PKCS8DIR); + cfg = settings_create(SWANCTL_CONF); + if (!cfg) + { + fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF); + return EINVAL; + } + + enumerator = cfg->create_section_enumerator(cfg, "secrets"); + while (enumerator->enumerate(enumerator, §ion)) + { + load_secrets(conn, cfg, section, raw); + } + enumerator->destroy(enumerator); + + cfg->destroy(cfg); + return 0; } From c1e413db49dd7d5df0bc54fe39c9f538411caeaf Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 15:49:21 +0100 Subject: [PATCH 15/38] swanctl: Support groups, certs and cacerts keywords --- src/swanctl/commands/load_conns.c | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 2c9884dc0..68ed3cda9 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -16,6 +16,7 @@ #define _GNU_SOURCE #include #include +#include #include "command.h" #include "swanctl.h" @@ -34,6 +35,28 @@ static bool is_list_key(char *key) "local_ts", "remote_ts", "vips", + "groups", + }; + int i; + + for (i = 0; i < countof(keys); i++) + { + if (strcaseeq(keys[i], key)) + { + return TRUE; + } + } + return FALSE; +} + +/** + * Check if we should handle a key as a list of comma separated files + */ +static bool is_file_list_key(char *key) +{ + char *keys[] = { + "certs", + "cacerts", }; int i; @@ -65,6 +88,49 @@ static void add_list_key(vici_req_t *req, char *key, char *value) vici_end_list(req); } +/** + * Add a vici list of blobs from a comma separated file list + */ +static void add_file_list_key(vici_req_t *req, char *key, char *value) +{ + enumerator_t *enumerator; + chunk_t *map; + char *token, buf[PATH_MAX]; + + vici_begin_list(req, key); + enumerator = enumerator_create_token(value, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (*token != '/') + { + if (streq(key, "certs")) + { + snprintf(buf, sizeof(buf), "%s/%s", SWANCTL_X509DIR, token); + token = buf; + } + if (streq(key, "cacerts")) + { + snprintf(buf, sizeof(buf), "%s/%s", SWANCTL_X509CADIR, 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 certificate '%s' failed: %s\n", + token, strerror(errno)); + } + } + enumerator->destroy(enumerator); + vici_end_list(req); +} + /** * Translate setting key/values from a section into vici key-values/lists */ @@ -80,6 +146,10 @@ static void add_key_values(vici_req_t *req, settings_t *cfg, char *section) { add_list_key(req, key, value); } + else if (is_file_list_key(key)) + { + add_file_list_key(req, key, value); + } else { vici_add_key_valuef(req, key, "%s", value); From da866234bbd62f6940e4d3c1ebd9c53d52e30058 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 16:11:57 +0100 Subject: [PATCH 16/38] swanctl: Register --version as last command --- src/swanctl/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index dbe1354c6..5cceb77dd 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -5,11 +5,11 @@ swanctl_SOURCES = \ commands/initiate.c \ commands/terminate.c \ commands/install.c \ - commands/version.c \ commands/list_sas.c \ commands/list_pols.c \ commands/load_conns.c \ commands/load_creds.c \ + commands/version.c \ swanctl.c swanctl.h swanctl_LDADD = \ From 51bdc1f3f179e7e75061693f850b6c3d192d7895 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 19 Feb 2014 16:48:04 +0100 Subject: [PATCH 17/38] swanctl: Add a list-conns command to query loaded connections --- src/swanctl/Makefile.am | 1 + src/swanctl/commands/list_conns.c | 219 ++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 src/swanctl/commands/list_conns.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 5cceb77dd..3db3bf914 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -7,6 +7,7 @@ swanctl_SOURCES = \ commands/install.c \ commands/list_sas.c \ commands/list_pols.c \ + commands/list_conns.c \ commands/load_conns.c \ commands/load_creds.c \ commands/version.c \ diff --git a/src/swanctl/commands/list_conns.c b/src/swanctl/commands/list_conns.c new file mode 100644 index 000000000..9eae557a4 --- /dev/null +++ b/src/swanctl/commands/list_conns.c @@ -0,0 +1,219 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include + +#include "command.h" + +#include + +/** + * Free hashtable with contained strings + */ +static void free_hashtable(hashtable_t *hashtable) +{ + enumerator_t *enumerator; + char *str; + + enumerator = hashtable->create_enumerator(hashtable); + while (enumerator->enumerate(enumerator, NULL, &str)) + { + free(str); + } + enumerator->destroy(enumerator); + + hashtable->destroy(hashtable); +} + +CALLBACK(values, int, + hashtable_t *sa, vici_res_t *res, char *name, void *value, int len) +{ + chunk_t chunk; + char *str; + + chunk = chunk_create(value, len); + if (chunk_printable(chunk, NULL, ' ')) + { + if (asprintf(&str, "%.*s", len, value) >= 0) + { + free(sa->put(sa, name, str)); + } + } + return 0; +} + + +CALLBACK(list, int, + hashtable_t *sa, vici_res_t *res, char *name, void *value, int len) +{ + chunk_t chunk; + char *str; + + chunk = chunk_create(value, len); + if (chunk_printable(chunk, NULL, ' ')) + { + str = sa->get(sa, name); + if (asprintf(&str, "%s%s%.*s", + str ?: "", str ? " " : "", len, value) >= 0) + { + free(sa->put(sa, name, str)); + } + } + return 0; +} + +CALLBACK(children_sn, int, + hashtable_t *ike, vici_res_t *res, char *name) +{ + hashtable_t *child; + int ret; + + child = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1); + ret = vici_parse_cb(res, NULL, values, list, child); + if (ret == 0) + { + printf(" %s: %s\n", name, child->get(child, "mode")); + printf(" local: %s\n", child->get(child, "local-ts")); + printf(" remote: %s\n", child->get(child, "remote-ts")); + } + free_hashtable(child); + return ret; +} + +CALLBACK(conn_sn, int, + hashtable_t *ike, vici_res_t *res, char *name) +{ + int ret = 0; + + if (streq(name, "children")) + { + return vici_parse_cb(res, children_sn, NULL, NULL, NULL); + } + if (streq(name, "local") || streq(name, "remote")) + { + hashtable_t *auth; + + auth = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1); + ret = vici_parse_cb(res, NULL, values, list, auth); + if (ret == 0) + { + printf(" %s %s authentication:\n", + name, auth->get(auth, "class") ?: "unspecified"); + if (auth->get(auth, "id")) + { + printf(" id: %s\n", auth->get(auth, "id")); + } + if (auth->get(auth, "groups")) + { + printf(" groups: %s\n", auth->get(auth, "groups")); + } + if (auth->get(auth, "certs")) + { + printf(" certs: %s\n", auth->get(auth, "certs")); + } + if (auth->get(auth, "cacerts")) + { + printf(" cacerts: %s\n", auth->get(auth, "cacerts")); + } + } + free_hashtable(auth); + } + return ret; +} + +CALLBACK(conns, int, + void *null, vici_res_t *res, char *name) +{ + printf("%s: %s\n", name, vici_find_str(res, "", "%s.version", name)); + + return vici_parse_cb(res, conn_sn, NULL, NULL, NULL); +} + +CALLBACK(list_cb, void, + bool *raw, char *name, vici_res_t *res) +{ + if (*raw) + { + vici_dump(res, "list-conn event", stdout); + } + else + { + if (vici_parse_cb(res, conns, NULL, NULL, NULL) != 0) + { + fprintf(stderr, "parsing conn event failed: %s\n", strerror(errno)); + } + } +} + +static int list_conns(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE; + char *arg; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --list-conns option"); + } + break; + } + if (vici_register(conn, "list-conn", list_cb, &raw) != 0) + { + fprintf(stderr, "registering for connections failed: %s\n", + strerror(errno)); + return errno; + } + req = vici_begin("list-conns"); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "list-conns request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "list-conns reply", stdout); + } + vici_free_res(res); + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + list_conns, 'L', "list-conns", "list loaded configurations", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From ebe78940aa05fc9fbb2e1127c25b1ae3c05bdd72 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 24 Feb 2014 13:28:24 +0100 Subject: [PATCH 18/38] swanctl: Be more verbose while loading connections and credentials --- src/swanctl/commands/load_conns.c | 4 ++++ src/swanctl/commands/load_creds.c | 24 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 68ed3cda9..020819687 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -214,6 +214,10 @@ static bool load_conn(vici_conn_t *conn, settings_t *cfg, section, vici_find_str(res, "", "errmsg")); ret = FALSE; } + else + { + printf("loaded connection '%s'\n", section); + } vici_free_res(res); return ret; } diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index dcfc6fe1f..52cdfb9ca 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -55,6 +55,10 @@ static bool load_cert(vici_conn_t *conn, bool raw, char *dir, dir, vici_find_str(res, "", "errmsg")); ret = FALSE; } + else + { + printf("loaded %s certificate '%s'\n", type, dir); + } vici_free_res(res); return ret; } @@ -124,6 +128,10 @@ static bool load_key(vici_conn_t *conn, bool raw, char *dir, dir, vici_find_str(res, "", "errmsg")); ret = FALSE; } + else + { + printf("loaded %s key '%s'\n", type, dir); + } vici_free_res(res); return ret; } @@ -274,6 +282,7 @@ static bool load_secret(vici_conn_t *conn, char *type, char *owners, vici_req_t *req; vici_res_t *res; chunk_t data; + char *owner; bool ret = TRUE; req = vici_begin("load-shared"); @@ -281,9 +290,9 @@ static bool load_secret(vici_conn_t *conn, char *type, char *owners, vici_add_key_valuef(req, "type", "%s", type); vici_begin_list(req, "owners"); enumerator = enumerator_create_token(owners, " ", " "); - while (enumerator->enumerate(enumerator, &owners)) + while (enumerator->enumerate(enumerator, &owner)) { - vici_add_list_itemf(req, "%s", owners); + vici_add_list_itemf(req, "%s", owner); } enumerator->destroy(enumerator); vici_end_list(req); @@ -319,6 +328,17 @@ static bool load_secret(vici_conn_t *conn, char *type, char *owners, vici_find_str(res, "", "errmsg")); ret = FALSE; } + else + { + printf("loaded %s secret for: ", type); + enumerator = enumerator_create_token(owners, " ", " "); + while (enumerator->enumerate(enumerator, &owner)) + { + printf("'%s' ", owner); + } + enumerator->destroy(enumerator); + printf("\n"); + } vici_free_res(res); return ret; } From 2d5c3a0f0f3c507489f60c798f5ada5bc2843646 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 24 Feb 2014 17:22:30 +0100 Subject: [PATCH 19/38] swanctl: Implement a --list-certs command to print or export daemon certificates --- src/swanctl/Makefile.am | 1 + src/swanctl/command.h | 2 +- src/swanctl/commands/list_certs.c | 668 ++++++++++++++++++++++++++++++ 3 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 src/swanctl/commands/list_certs.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 3db3bf914..c951b1587 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -8,6 +8,7 @@ swanctl_SOURCES = \ commands/list_sas.c \ commands/list_pols.c \ commands/list_conns.c \ + commands/list_certs.c \ commands/load_conns.c \ commands/load_creds.c \ commands/version.c \ diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 699483bf1..369e16be6 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 11 +#define MAX_COMMANDS 12 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/list_certs.c b/src/swanctl/commands/list_certs.c new file mode 100644 index 000000000..d9b773892 --- /dev/null +++ b/src/swanctl/commands/list_certs.c @@ -0,0 +1,668 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "command.h" + +typedef enum { + FORMAT_RAW = (1<<0), + FORMAT_PEM = (1<<1), +} format_options_t; + +/** + * Print PEM encoding of a certificate + */ +static void print_pem(certificate_t *cert) +{ + chunk_t encoding; + + if (cert->get_encoding(cert, CERT_PEM, &encoding)) + { + printf("%.*s", (int)encoding.len, encoding.ptr); + free(encoding.ptr); + } + else + { + fprintf(stderr, "PEM encoding certificate failed\n"); + } +} + +/** + * Print public key information + */ +static void print_pubkey(public_key_t *key, bool has_privkey) +{ + chunk_t chunk; + + printf("pubkey: %N %d bits", key_type_names, key->get_type(key), + key->get_keysize(key)); + if (has_privkey) + { + printf(", has private key"); + } + printf("\n"); + if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &chunk)) + { + printf("keyid: %#B\n", &chunk); + } + if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &chunk)) + { + printf("subjkey: %#B\n", &chunk); + } +} + +/** + * Print X509 specific certificate information + */ +static void print_x509(x509_t *x509) +{ + enumerator_t *enumerator; + identification_t *id; + traffic_selector_t *block; + chunk_t chunk; + bool first; + char *uri; + int len, explicit, inhibit; + x509_flag_t flags; + x509_cdp_t *cdp; + x509_cert_policy_t *policy; + x509_policy_mapping_t *mapping; + + chunk = chunk_skip_zero(x509->get_serial(x509)); + printf("serial: %#B\n", &chunk); + + first = TRUE; + enumerator = x509->create_subjectAltName_enumerator(x509); + while (enumerator->enumerate(enumerator, &id)) + { + if (first) + { + printf("altNames: "); + first = FALSE; + } + else + { + printf(", "); + } + printf("%Y", id); + } + if (!first) + { + printf("\n"); + } + enumerator->destroy(enumerator); + + flags = x509->get_flags(x509); + printf("flags: "); + if (flags & X509_CA) + { + printf("CA "); + } + if (flags & X509_CRL_SIGN) + { + printf("CRLSign "); + } + if (flags & X509_AA) + { + printf("AA "); + } + if (flags & X509_OCSP_SIGNER) + { + printf("OCSP "); + } + if (flags & X509_AA) + { + printf("AA "); + } + if (flags & X509_SERVER_AUTH) + { + printf("serverAuth "); + } + if (flags & X509_CLIENT_AUTH) + { + printf("clientAuth "); + } + if (flags & X509_IKE_INTERMEDIATE) + { + printf("iKEIntermediate "); + } + if (flags & X509_SELF_SIGNED) + { + printf("self-signed "); + } + printf("\n"); + + first = TRUE; + enumerator = x509->create_crl_uri_enumerator(x509); + while (enumerator->enumerate(enumerator, &cdp)) + { + if (first) + { + printf("CRL URIs: %s", cdp->uri); + first = FALSE; + } + else + { + printf(" %s", cdp->uri); + } + if (cdp->issuer) + { + printf(" (CRL issuer: %Y)", cdp->issuer); + } + printf("\n"); + } + enumerator->destroy(enumerator); + + first = TRUE; + enumerator = x509->create_ocsp_uri_enumerator(x509); + while (enumerator->enumerate(enumerator, &uri)) + { + if (first) + { + printf("OCSP URIs: %s\n", uri); + first = FALSE; + } + else + { + printf(" %s\n", uri); + } + } + enumerator->destroy(enumerator); + + len = x509->get_constraint(x509, X509_PATH_LEN); + if (len != X509_NO_CONSTRAINT) + { + printf("pathlen: %d\n", len); + } + + first = TRUE; + enumerator = x509->create_name_constraint_enumerator(x509, TRUE); + while (enumerator->enumerate(enumerator, &id)) + { + if (first) + { + printf("Permitted NameConstraints:\n"); + first = FALSE; + } + printf(" %Y\n", id); + } + enumerator->destroy(enumerator); + first = TRUE; + enumerator = x509->create_name_constraint_enumerator(x509, FALSE); + while (enumerator->enumerate(enumerator, &id)) + { + if (first) + { + printf("Excluded NameConstraints:\n"); + first = FALSE; + } + printf(" %Y\n", id); + } + enumerator->destroy(enumerator); + + first = TRUE; + enumerator = x509->create_cert_policy_enumerator(x509); + while (enumerator->enumerate(enumerator, &policy)) + { + char *oid; + + if (first) + { + printf("CertificatePolicies:\n"); + first = FALSE; + } + oid = asn1_oid_to_string(policy->oid); + if (oid) + { + printf(" %s\n", oid); + free(oid); + } + else + { + printf(" %#B\n", &policy->oid); + } + if (policy->cps_uri) + { + printf(" CPS: %s\n", policy->cps_uri); + } + if (policy->unotice_text) + { + printf(" Notice: %s\n", policy->unotice_text); + + } + } + enumerator->destroy(enumerator); + + first = TRUE; + enumerator = x509->create_policy_mapping_enumerator(x509); + while (enumerator->enumerate(enumerator, &mapping)) + { + char *issuer_oid, *subject_oid; + + if (first) + { + printf("PolicyMappings:\n"); + first = FALSE; + } + issuer_oid = asn1_oid_to_string(mapping->issuer); + subject_oid = asn1_oid_to_string(mapping->subject); + printf(" %s => %s\n", issuer_oid, subject_oid); + free(issuer_oid); + free(subject_oid); + } + enumerator->destroy(enumerator); + + explicit = x509->get_constraint(x509, X509_REQUIRE_EXPLICIT_POLICY); + inhibit = x509->get_constraint(x509, X509_INHIBIT_POLICY_MAPPING); + len = x509->get_constraint(x509, X509_INHIBIT_ANY_POLICY); + + if (explicit != X509_NO_CONSTRAINT || inhibit != X509_NO_CONSTRAINT || + len != X509_NO_CONSTRAINT) + { + printf("PolicyConstraints:\n"); + if (explicit != X509_NO_CONSTRAINT) + { + printf(" requireExplicitPolicy: %d\n", explicit); + } + if (inhibit != X509_NO_CONSTRAINT) + { + printf(" inhibitPolicyMapping: %d\n", inhibit); + } + if (len != X509_NO_CONSTRAINT) + { + printf(" inhibitAnyPolicy: %d\n", len); + } + } + + chunk = x509->get_authKeyIdentifier(x509); + if (chunk.ptr) + { + printf("authkeyId: %#B\n", &chunk); + } + + chunk = x509->get_subjectKeyIdentifier(x509); + if (chunk.ptr) + { + printf("subjkeyId: %#B\n", &chunk); + } + if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS) + { + first = TRUE; + printf("addresses: "); + enumerator = x509->create_ipAddrBlock_enumerator(x509); + while (enumerator->enumerate(enumerator, &block)) + { + if (first) + { + first = FALSE; + } + else + { + printf(", "); + } + printf("%R", block); + } + enumerator->destroy(enumerator); + printf("\n"); + } +} + +/** + * Print CRL specific information + */ +static void print_crl(crl_t *crl) +{ + enumerator_t *enumerator; + time_t ts; + crl_reason_t reason; + chunk_t chunk; + int count = 0; + bool first; + char buf[64]; + struct tm tm; + x509_cdp_t *cdp; + + chunk = chunk_skip_zero(crl->get_serial(crl)); + printf("serial: %#B\n", &chunk); + + if (crl->is_delta_crl(crl, &chunk)) + { + chunk = chunk_skip_zero(chunk); + printf("delta CRL: for serial %#B\n", &chunk); + } + chunk = crl->get_authKeyIdentifier(crl); + printf("authKeyId: %#B\n", &chunk); + + first = TRUE; + enumerator = crl->create_delta_crl_uri_enumerator(crl); + while (enumerator->enumerate(enumerator, &cdp)) + { + if (first) + { + printf("freshest: %s", cdp->uri); + first = FALSE; + } + else + { + printf(" %s", cdp->uri); + } + if (cdp->issuer) + { + printf(" (CRL issuer: %Y)", cdp->issuer); + } + printf("\n"); + } + enumerator->destroy(enumerator); + + enumerator = crl->create_enumerator(crl); + while (enumerator->enumerate(enumerator, &chunk, &ts, &reason)) + { + count++; + } + enumerator->destroy(enumerator); + + printf("%d revoked certificate%s%s\n", count, + count == 1 ? "" : "s", count ? ":" : ""); + enumerator = crl->create_enumerator(crl); + while (enumerator->enumerate(enumerator, &chunk, &ts, &reason)) + { + chunk = chunk_skip_zero(chunk); + localtime_r(&ts, &tm); + strftime(buf, sizeof(buf), "%F %T", &tm); + printf(" %#B %N %s\n", &chunk, crl_reason_names, reason, buf); + count++; + } + enumerator->destroy(enumerator); +} + +/** + * Print AC specific information + */ +static void print_ac(ac_t *ac) +{ + ac_group_type_t type; + identification_t *id; + enumerator_t *groups; + chunk_t chunk; + bool first = TRUE; + + chunk = chunk_skip_zero(ac->get_serial(ac)); + printf("serial: %#B\n", &chunk); + + id = ac->get_holderIssuer(ac); + if (id) + { + printf("hissuer: \"%Y\"\n", id); + } + chunk = chunk_skip_zero(ac->get_holderSerial(ac)); + if (chunk.ptr) + { + printf("hserial: %#B\n", &chunk); + } + groups = ac->create_group_enumerator(ac); + while (groups->enumerate(groups, &type, &chunk)) + { + int oid; + char *str; + + if (first) + { + printf("groups: "); + first = FALSE; + } + else + { + printf(" "); + } + switch (type) + { + case AC_GROUP_TYPE_STRING: + printf("%.*s", (int)chunk.len, chunk.ptr); + break; + case AC_GROUP_TYPE_OID: + oid = asn1_known_oid(chunk); + if (oid == OID_UNKNOWN) + { + str = asn1_oid_to_string(chunk); + if (str) + { + printf("%s", str); + free(str); + } + else + { + printf("OID:%#B", &chunk); + } + } + else + { + printf("%s", oid_names[oid].name); + } + break; + case AC_GROUP_TYPE_OCTETS: + printf("%#B", &chunk); + break; + } + printf("\n"); + } + groups->destroy(groups); + + chunk = ac->get_authKeyIdentifier(ac); + if (chunk.ptr) + { + printf("authkey: %#B\n", &chunk); + } +} + +/** + * Print certificate information + */ +static void print_cert(certificate_t *cert, bool has_privkey) +{ + time_t now, notAfter, notBefore; + public_key_t *key; + + now = time(NULL); + + printf("cert: %N\n", certificate_type_names, cert->get_type(cert)); + if (cert->get_type(cert) != CERT_X509_CRL) + { + printf("subject: \"%Y\"\n", cert->get_subject(cert)); + } + printf("issuer: \"%Y\"\n", cert->get_issuer(cert)); + + cert->get_validity(cert, &now, ¬Before, ¬After); + printf("validity: not before %T, ", ¬Before, FALSE); + if (now < notBefore) + { + printf("not valid yet (valid in %V)\n", &now, ¬Before); + } + else + { + printf("ok\n"); + } + printf(" not after %T, ", ¬After, FALSE); + if (now > notAfter) + { + printf("expired (%V ago)\n", &now, ¬After); + } + else + { + printf("ok (expires in %V)\n", &now, ¬After); + } + + switch (cert->get_type(cert)) + { + case CERT_X509: + print_x509((x509_t*)cert); + break; + case CERT_X509_CRL: + print_crl((crl_t*)cert); + break; + case CERT_X509_AC: + print_ac((ac_t*)cert); + break; + default: + fprintf(stderr, "parsing certificate subtype %N not implemented\n", + certificate_type_names, cert->get_type(cert)); + break; + } + key = cert->get_public_key(cert); + if (key) + { + print_pubkey(key, has_privkey); + key->destroy(key); + } + printf("\n"); +} + +CALLBACK(list_cb, void, + format_options_t *format, char *name, vici_res_t *res) +{ + if (*format & FORMAT_RAW) + { + vici_dump(res, "list-cert event", stdout); + } + else + { + certificate_type_t type; + certificate_t *cert; + void *buf; + int len; + bool has_privkey; + + buf = vici_find(res, &len, "data"); + type = enum_from_name(certificate_type_names, + vici_find_str(res, "ANY", "type")); + has_privkey = streq(vici_find_str(res, "no", "has_privkey"), "yes"); + if (type != -1 && type != CERT_ANY && buf) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, type, + BUILD_BLOB_ASN1_DER, chunk_create(buf, len), + BUILD_END); + if (cert) + { + if (*format & FORMAT_PEM) + { + print_pem(cert); + } + else + { + print_cert(cert, has_privkey); + } + cert->destroy(cert); + } + else + { + fprintf(stderr, "parsing certificate failed\n"); + } + } + else + { + fprintf(stderr, "received incomplete certificate data\n"); + } + } +} + +static int list_certs(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + format_options_t format = 0; + char *arg, *subject = NULL, *type = NULL; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 's': + subject = arg; + continue; + case 't': + type = arg; + continue; + case 'p': + format |= FORMAT_PEM; + continue; + case 'r': + format |= FORMAT_RAW; + continue; + case EOF: + break; + default: + return command_usage("invalid --list-certs option"); + } + break; + } + if (vici_register(conn, "list-cert", list_cb, &format) != 0) + { + fprintf(stderr, "registering for certificates failed: %s\n", + strerror(errno)); + return errno; + } + req = vici_begin("list-certs"); + if (type) + { + vici_add_key_valuef(req, "type", "%s", type); + } + if (subject) + { + vici_add_key_valuef(req, "subject", "%s", subject); + } + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "list-certs request failed: %s\n", strerror(errno)); + return errno; + } + if (format & FORMAT_RAW) + { + vici_dump(res, "list-certs reply", stdout); + } + vici_free_res(res); + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + list_certs, 'x', "list-certs", "list stored certificates", + {"[--subject ] [--type X509|X509_AC|X509_CRL] [--pem] [--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"subject", 's', 1, "filter by certificate subject"}, + {"type", 't', 1, "filter by certificate type"}, + {"pem", 'p', 0, "print PEM encoding of certificate"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From 3b22e8e995f62a5884a4e0c0ff8aa5e1aacfb5f6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 5 Mar 2014 12:15:24 +0100 Subject: [PATCH 20/38] swanctl: Add a swanctl.conf template file --- src/swanctl/Makefile.am | 2 + src/swanctl/swanctl.conf | 122 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/swanctl/swanctl.conf diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index c951b1587..d731c0467 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -20,6 +20,8 @@ swanctl_LDADD = \ swanctl.o : $(top_builddir)/config.status +EXTRA_DIST = swanctl.conf + AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libcharon/plugins/vici \ diff --git a/src/swanctl/swanctl.conf b/src/swanctl/swanctl.conf new file mode 100644 index 000000000..7580740b1 --- /dev/null +++ b/src/swanctl/swanctl.conf @@ -0,0 +1,122 @@ +connections { + +# # an IKE configuration named conn1 +# conn1 { +# # IKE version to use +# version = 2 +# # list of acceptable local addresses/subnets +# local_addrs = 0.0.0.0 +# # peer address, additional addresses/subnets as responder +# remote_addrs = 192.168.5.1 +# # local UPD port for IKE +# local_port = 500 +# # remote UDP port for IKE +# remote_port = 500 +# # Proposals for IKE, "default" is the default proposal +# proposals = aes128gcm16-prfsha256-modp2048, default +# # virtual IPs to request, such as 0.0.0.0 or :: +# vips = +# # IKEv1 aggressive mode +# aggressive = no +# # use of pull/push in IKEv1 mode config +# pull = yes +# # enforce UDP encapsulation by faking NAT-D payloads +# encap = no +# # enable IKEv2 MOBIKE +# mobike = yes +# # interval of liveness checks +# dpd_delay = 10s +# # timeout for DPD checks (IKEV1 only) +# dpd_timeout = 30s +# # use IKEv1 UDP packet fragmentation +# fragmentation = force +# # send certificate requests +# send_certreq = yes +# # send certificate payloads +# send_cert = ifasked +# # number of retransmission sequences to do before givin up +# keyingtries = 0 +# # uniquness policy, never|no|keep|replace| +# unique = no +# # time to schedule IKE reauthentication +# reauth_time = 3h +# # time to schedule IKE rekeying +# rekey_time = 2h +# # hard IKE_SA lifetime if rekey/reauth does not complete +# over_time = 10m +# # range of random time to subtract from rekey/rauth times +# rand_time = 10m +# +# # local authentication, first round +# local { +# # additional certificates to load +# certs = a.pem, xy.der +# # authentication to perform locally +# auth = pubkey +# # IKE identity for local +# id = win@strongswan.org +# # Client EAP-Identity to use +# eap_id = moon +# # Server side EAP identity to use, EAP-TTLS etc. +# aaa_identity = srv +# # IKEv1 XAuth username +# xauth_id = moon +# } +# # remote authentication, first round +# remote { +# # IKE identity for peer +# id = %any +# # list of acceptable peer certificates +# certs = client.pem +# # list of acceptable CA certificates +# cacert = ca.der +# # revocation policy, strict|ifuri +# revocation = ifuri +# # authentication to expect from remote +# auth = pubkey +# } +# children { +# # First CHILD_SA configuration +# child1 { +# # AH proposals to offer +# ah_proposals = default +# # ESP proposals to offer +# esp_proposals = aes128gcm16-modp2048, default +# # local subnets to tunnel +# local_ts = 192.168.3.0/24 +# # remote subnets to tunnel +# remote_ts = 192.168.1.0/24 +# # updown script to invoke +# updown = path-to-script +# # hostaccess variable to pass to updown +# hostaccess = yes +# # IPsec mode, tunnel|transport|pass|drop +# mode = tunnel +# # action to perform on DPD timeout +# dpd_action = restart +# # enable IPComp +# ipcomp = no +# # inactivity timeout before closing CHILD_SA +# inactivity = 2m +# # fixed reqid to use for this CHILD_SA +# reqid = 5 +# # Netfilter mark for input traffic +# mark_in = 1 +# # Netfilter mark for output traffic +# mark_out = 5/0xffffffff +# # Traffic Flow Confidentiality padding +# tfc_padding = 1500 +# } +# } +# } + +} + +secrets { + eap { +# tester = testpassword + } + ike { +# sun.strongswan.org = 0x12345678901234 + } +} From 7b35c02db4b655524d044f4bcc0d2b69d12cf937 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 6 Mar 2014 10:56:50 +0100 Subject: [PATCH 21/38] swanctl: Implement a --log command to trace debugging log --- src/swanctl/Makefile.am | 1 + src/swanctl/command.h | 2 +- src/swanctl/commands/log.c | 96 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/swanctl/commands/log.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index d731c0467..47389e96b 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -11,6 +11,7 @@ swanctl_SOURCES = \ commands/list_certs.c \ commands/load_conns.c \ commands/load_creds.c \ + commands/log.c \ commands/version.c \ swanctl.c swanctl.h diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 369e16be6..4c7370fce 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 12 +#define MAX_COMMANDS 13 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/log.c b/src/swanctl/commands/log.c new file mode 100644 index 000000000..4810025d4 --- /dev/null +++ b/src/swanctl/commands/log.c @@ -0,0 +1,96 @@ +/* + * 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 + * 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 +#include + +CALLBACK(log_cb, void, + bool *raw, char *name, vici_res_t *msg) +{ + if (*raw) + { + vici_dump(msg, "log", stdout); + } + else + { + char *current, *next; + + current = vici_find_str(msg, NULL, "msg"); + while (current) + { + next = strchr(current, '\n'); + printf("%.2d[%s] ", vici_find_int(msg, 0, "thread"), + vici_find_str(msg, " ", "group")); + if (next == NULL) + { + printf("%s\n", current); + break; + } + printf("%.*s\n", (int)(next - current), current); + current = next + 1; + } + } +} + +static int logcmd(vici_conn_t *conn) +{ + bool raw = FALSE; + char *arg; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --log option"); + } + break; + } + + if (vici_register(conn, "log", log_cb, &raw) != 0) + { + fprintf(stderr, "registering for log failed: %s\n", strerror(errno)); + return errno; + } + while (TRUE) + { + sleep(1); + } + return 0; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + logcmd, 'T', "log", "trace logging output", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From 250c6e3d9028a66d373bb1f6550683e0962ceec9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 9 Apr 2014 13:25:13 +0200 Subject: [PATCH 22/38] swanctl: Fix enumeration of registered commands if MAX_COMMANDS is hit --- src/swanctl/command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/swanctl/command.c b/src/swanctl/command.c index 29f6be97f..e488273bf 100644 --- a/src/swanctl/command.c +++ b/src/swanctl/command.c @@ -80,7 +80,7 @@ static void build_opts() memset(command_optstring, 0, sizeof(command_optstring)); if (active == help_idx) { - for (i = 0; cmds[i].cmd; i++) + for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { command_opts[i].name = cmds[i].cmd; command_opts[i].val = cmds[i].op; @@ -218,7 +218,7 @@ int command_usage(char *error, ...) fprintf(out, "usage:\n"); if (active == help_idx) { - for (i = 0; cmds[i].cmd; i++) + for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { fprintf(out, " swanctl --%-10s (-%c) %s\n", cmds[i].cmd, cmds[i].op, cmds[i].description); @@ -292,7 +292,7 @@ int command_dispatch(int c, char *v[]) build_opts(); op = getopt_long(c, v, command_optstring, command_opts, NULL); - for (i = 0; cmds[i].cmd; i++) + for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { if (cmds[i].op == op) { From 4ee33b44df5283127439856b18daee1bcc39a285 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 15 Apr 2014 13:33:11 +0200 Subject: [PATCH 23/38] swanctl: Encode connection "pools" as list items --- src/swanctl/commands/load_conns.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 020819687..d418cd3a4 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -35,6 +35,7 @@ static bool is_list_key(char *key) "local_ts", "remote_ts", "vips", + "pools", "groups", }; int i; From a77acc183a263acd383cbb64b9613443b8d21f29 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Apr 2014 11:20:27 +0200 Subject: [PATCH 24/38] swanctl: Add a load-pools command to (re-)load pool configurations from file --- src/swanctl/Makefile.am | 1 + src/swanctl/command.h | 2 +- src/swanctl/commands/load_pools.c | 283 ++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 src/swanctl/commands/load_pools.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 47389e96b..bacd65fe2 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -11,6 +11,7 @@ swanctl_SOURCES = \ commands/list_certs.c \ commands/load_conns.c \ commands/load_creds.c \ + commands/load_pools.c \ commands/log.c \ commands/version.c \ swanctl.c swanctl.h diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 4c7370fce..fc6c82cd5 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 13 +#define MAX_COMMANDS 14 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/load_pools.c b/src/swanctl/commands/load_pools.c new file mode 100644 index 000000000..1224021a7 --- /dev/null +++ b/src/swanctl/commands/load_pools.c @@ -0,0 +1,283 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "command.h" +#include "swanctl.h" + +/** + * Add a vici list from a comma separated string value + */ +static void add_list_key(vici_req_t *req, char *key, char *value) +{ + enumerator_t *enumerator; + char *token; + + vici_begin_list(req, key); + enumerator = enumerator_create_token(value, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + vici_add_list_itemf(req, "%s", token); + } + enumerator->destroy(enumerator); + vici_end_list(req); +} + +/** + * Translate setting key/values from a section into vici key-values/lists + */ +static void add_key_values(vici_req_t *req, settings_t *cfg, char *section) +{ + enumerator_t *enumerator; + char *key, *value; + + 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, "addrs")) + { + vici_add_key_valuef(req, key, "%s", value); + } + else + { + add_list_key(req, key, value); + } + } + enumerator->destroy(enumerator); +} + +/** + * Load a pool configuration + */ +static bool load_pool(vici_conn_t *conn, settings_t *cfg, + char *section, bool raw) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + char buf[128]; + + snprintf(buf, sizeof(buf), "%s.%s", "pools", section); + + req = vici_begin("load-pool"); + + vici_begin_section(req, section); + add_key_values(req, cfg, buf); + vici_end_section(req); + + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "load-pool request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "load-pool reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "loading pool '%s' failed: %s\n", + section, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + else + { + printf("loaded pool '%s'\n", section); + } + vici_free_res(res); + return ret; +} + +CALLBACK(list_pool, int, + linked_list_t *list, vici_res_t *res, char *name) +{ + list->insert_last(list, strdup(name)); + return 0; +} + +/** + * Create a list of currently loaded pools + */ +static linked_list_t* list_pools(vici_conn_t *conn, bool raw) +{ + linked_list_t *list; + vici_res_t *res; + + list = linked_list_create(); + + res = vici_submit(vici_begin("get-pools"), conn); + if (res) + { + if (raw) + { + vici_dump(res, "get-pools reply", stdout); + } + vici_parse_cb(res, list_pool, NULL, NULL, list); + vici_free_res(res); + } + return list; +} + +/** + * Remove and free a string from a list + */ +static void remove_from_list(linked_list_t *list, char *str) +{ + enumerator_t *enumerator; + char *current; + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current, str)) + { + list->remove_at(list, enumerator); + free(current); + } + } + enumerator->destroy(enumerator); +} + +/** + * Unload a pool by name + */ +static bool unload_pool(vici_conn_t *conn, char *name, bool raw) +{ + vici_req_t *req; + vici_res_t *res; + bool ret = TRUE; + + req = vici_begin("unload-pool"); + vici_add_key_valuef(req, "name", "%s", name); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "unload-pool request failed: %s\n", strerror(errno)); + return FALSE; + } + if (raw) + { + vici_dump(res, "unload-pool reply", stdout); + } + else if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "unloading pool '%s' failed: %s\n", + name, vici_find_str(res, "", "errmsg")); + ret = FALSE; + } + vici_free_res(res); + return ret; +} + +static int load_pools(vici_conn_t *conn) +{ + bool raw = FALSE; + u_int found = 0, loaded = 0, unloaded = 0; + char *arg, *section; + enumerator_t *enumerator; + linked_list_t *pools; + settings_t *cfg; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --load-pools option"); + } + break; + } + + cfg = settings_create(SWANCTL_CONF); + if (!cfg) + { + fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF); + return EINVAL; + } + + pools = list_pools(conn, raw); + + enumerator = cfg->create_section_enumerator(cfg, "pools"); + while (enumerator->enumerate(enumerator, §ion)) + { + remove_from_list(pools, section); + found++; + if (load_pool(conn, cfg, section, raw)) + { + loaded++; + } + } + enumerator->destroy(enumerator); + + cfg->destroy(cfg); + + /* unload all pools in daemon, but not in file */ + while (pools->remove_first(pools, (void**)§ion) == SUCCESS) + { + if (unload_pool(conn, section, raw)) + { + unloaded++; + } + free(section); + } + pools->destroy(pools); + + if (raw) + { + return 0; + } + if (found == 0) + { + printf("no pools found, %u unloaded\n", unloaded); + return 0; + } + if (loaded == found) + { + printf("successfully loaded %u pools, %u unloaded\n", + loaded, unloaded); + return 0; + } + fprintf(stderr, "loaded %u of %u pools, %u failed to load, " + "%u unloaded\n", loaded, found, found - loaded, unloaded); + return EINVAL; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + load_pools, 'a', "load-pools", "(re-)load pool configuration", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From 43306afe8e84b36c99c9e56580649bda99a987ba Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Apr 2014 12:07:14 +0200 Subject: [PATCH 25/38] swanctl: Add a list-pools command to summarize pool status --- src/swanctl/Makefile.am | 1 + src/swanctl/command.h | 2 +- src/swanctl/commands/list_pools.c | 96 +++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/swanctl/commands/list_pools.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index bacd65fe2..1f702e153 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -9,6 +9,7 @@ swanctl_SOURCES = \ commands/list_pols.c \ commands/list_conns.c \ commands/list_certs.c \ + commands/list_pools.c \ commands/load_conns.c \ commands/load_creds.c \ commands/load_pools.c \ diff --git a/src/swanctl/command.h b/src/swanctl/command.h index fc6c82cd5..a394796d1 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 14 +#define MAX_COMMANDS 15 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/list_pools.c b/src/swanctl/commands/list_pools.c new file mode 100644 index 000000000..beff65b96 --- /dev/null +++ b/src/swanctl/commands/list_pools.c @@ -0,0 +1,96 @@ +/* + * 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 + * 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. + */ + +#define _GNU_SOURCE +#include +#include + +#include "command.h" + +CALLBACK(list_pool, int, + linked_list_t *list, vici_res_t *res, char *name) +{ + char pool[64], leases[32]; + + snprintf(pool, sizeof(pool), "%s:", name); + snprintf(leases, sizeof(leases), "%s / %s / %s", + vici_find_str(res, "", "%s.online", name), + vici_find_str(res, "", "%s.offline", name), + vici_find_str(res, "", "%s.size", name)); + + printf("%-20s %-30s %16s\n", + name, vici_find_str(res, "", "%s.base", name), leases); + + return 0; +} + +static int list_pools(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + bool raw = FALSE; + char *arg; + int ret = 0; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'r': + raw = TRUE; + continue; + case EOF: + break; + default: + return command_usage("invalid --list-pools option"); + } + break; + } + + req = vici_begin("get-pools"); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "get-pools request failed: %s\n", strerror(errno)); + return errno; + } + if (raw) + { + vici_dump(res, "get-pools reply", stdout); + } + else + { + ret = vici_parse_cb(res, list_pool, NULL, NULL, NULL); + } + vici_free_res(res); + return ret; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + list_pools, 'A', "list-pools", "list loaded pool configurations", + {"[--raw]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + } + }); +} From a2875525ae6bc000cbd4f877f4957e2f258ac16e Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Apr 2014 14:55:43 +0200 Subject: [PATCH 26/38] swanctl: List local and remote addresses in list-conns --- src/swanctl/commands/list_conns.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/swanctl/commands/list_conns.c b/src/swanctl/commands/list_conns.c index 9eae557a4..4a7cd3583 100644 --- a/src/swanctl/commands/list_conns.c +++ b/src/swanctl/commands/list_conns.c @@ -135,12 +135,29 @@ CALLBACK(conn_sn, int, return ret; } +CALLBACK(conn_list, int, + hashtable_t *sa, vici_res_t *res, char *name, void *value, int len) +{ + if (chunk_printable(chunk_create(value, len), NULL, ' ')) + { + if (streq(name, "local_addrs")) + { + printf(" local: %.*s\n", len, value); + } + if (streq(name, "remote_addrs")) + { + printf(" remote: %.*s\n", len, value); + } + } + return 0; +} + CALLBACK(conns, int, void *null, vici_res_t *res, char *name) { printf("%s: %s\n", name, vici_find_str(res, "", "%s.version", name)); - return vici_parse_cb(res, conn_sn, NULL, NULL, NULL); + return vici_parse_cb(res, conn_sn, NULL, conn_list, NULL); } CALLBACK(list_cb, void, From 1312eab0368f945c25a6ceb29208f6efaedd8b50 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 25 Apr 2014 11:22:45 +0200 Subject: [PATCH 27/38] swanctl: Change syntax of secrets to accept identities with special chars Having identity strings in the settings key is problematic, as the parser can't handle arbitrary characters in it. Further, the space separation makes it impossible to define identities with spaces. The new format uses key prefixes, similar to those used in local/remote auth sections of connections. The secrets section takes subsections with type prefixes, and each subsection uses "id" prefixes to define an arbitrary number of identities. --- src/swanctl/commands/load_creds.c | 86 +++++++++++++++++-------------- src/swanctl/swanctl.conf | 15 +++--- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index 52cdfb9ca..94d31f49f 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -273,30 +273,44 @@ static void load_keys(vici_conn_t *conn, bool raw, bool noprompt, } /** - * Load a single secret for ids over VICI + * Load a single secret over VICI */ -static bool load_secret(vici_conn_t *conn, char *type, char *owners, - char *value, bool raw) +static bool load_secret(vici_conn_t *conn, settings_t *cfg, + char *section, bool raw) { enumerator_t *enumerator; vici_req_t *req; vici_res_t *res; chunk_t data; - char *owner; + char *key, *value, buf[128], *type = NULL; bool ret = TRUE; + int i; + char *types[] = { + "eap", + "xauth", + "ike", + }; - req = vici_begin("load-shared"); - - vici_add_key_valuef(req, "type", "%s", type); - vici_begin_list(req, "owners"); - enumerator = enumerator_create_token(owners, " ", " "); - while (enumerator->enumerate(enumerator, &owner)) + for (i = 0; i < countof(types); i++) { - vici_add_list_itemf(req, "%s", owner); + if (strpfx(section, types[i])) + { + type = types[i]; + break; + } + } + if (!type) + { + fprintf(stderr, "ignoring unsupported secret '%s'\n", section); + return FALSE; } - enumerator->destroy(enumerator); - vici_end_list(req); + value = cfg->get_str(cfg, "secrets.%s.secret", NULL, section); + if (!value) + { + fprintf(stderr, "missing secret in '%s', ignored\n", section); + return FALSE; + } if (strcasepfx(value, "0x")) { data = chunk_from_hex(chunk_from_str(value + 2), NULL); @@ -309,9 +323,26 @@ static bool load_secret(vici_conn_t *conn, char *type, char *owners, { data = chunk_clone(chunk_from_str(value)); } + + req = vici_begin("load-shared"); + + vici_add_key_valuef(req, "type", "%s", type); vici_add_key_value(req, "data", data.ptr, data.len); chunk_clear(&data); + vici_begin_list(req, "owners"); + snprintf(buf, sizeof(buf), "secrets.%s", section); + enumerator = cfg->create_key_value_enumerator(cfg, buf); + while (enumerator->enumerate(enumerator, &key, &value)) + { + if (strpfx(key, "id")) + { + vici_add_list_itemf(req, "%s", value); + } + } + enumerator->destroy(enumerator); + vici_end_list(req); + res = vici_submit(req, conn); if (!res) { @@ -330,37 +361,12 @@ static bool load_secret(vici_conn_t *conn, char *type, char *owners, } else { - printf("loaded %s secret for: ", type); - enumerator = enumerator_create_token(owners, " ", " "); - while (enumerator->enumerate(enumerator, &owner)) - { - printf("'%s' ", owner); - } - enumerator->destroy(enumerator); - printf("\n"); + printf("loaded %s secret '%s'\n", type, section); } vici_free_res(res); return ret; } -/** - * Load secrets from settings section - */ -static void load_secrets(vici_conn_t *conn, settings_t *cfg, - char *section, bool raw) -{ - enumerator_t *enumerator; - char buf[64], *key, *value; - - snprintf(buf, sizeof(buf), "secrets.%s", section); - enumerator = cfg->create_key_value_enumerator(cfg, buf); - while (enumerator->enumerate(enumerator, &key, &value)) - { - load_secret(conn, section, key, value, raw); - } - enumerator->destroy(enumerator); -} - /** * Clear all currently loaded credentials */ @@ -440,7 +446,7 @@ static int load_creds(vici_conn_t *conn) enumerator = cfg->create_section_enumerator(cfg, "secrets"); while (enumerator->enumerate(enumerator, §ion)) { - load_secrets(conn, cfg, section, raw); + load_secret(conn, cfg, section, raw); } enumerator->destroy(enumerator); diff --git a/src/swanctl/swanctl.conf b/src/swanctl/swanctl.conf index 7580740b1..f43d1d49b 100644 --- a/src/swanctl/swanctl.conf +++ b/src/swanctl/swanctl.conf @@ -113,10 +113,13 @@ connections { } secrets { - eap { -# tester = testpassword - } - ike { -# sun.strongswan.org = 0x12345678901234 - } +# eap-tester { +# id = tester +# secret = test +# } +# ike-moon { +# id-local = sun.strongswan.org +# id-remote = mon.strongswan.org +# secret = 0x12345678901234 +# } } From 49d8a5f554540586ef65f2e2a8ebc62aa0e25e8a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 17 Apr 2014 18:34:38 +0200 Subject: [PATCH 28/38] swanctl: Install swanctl.conf if it does not exist yet --- src/swanctl/Makefile.am | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 1f702e153..4f0c1c2e6 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -23,10 +23,14 @@ swanctl_LDADD = \ swanctl.o : $(top_builddir)/config.status -EXTRA_DIST = swanctl.conf - AM_CPPFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libcharon/plugins/vici \ -DSWANCTLDIR=\""${swanctldir}\"" \ -DPLUGINS=\""${s_plugins}\"" + +EXTRA_DIST = swanctl.conf + +install-data-local: swanctl.conf + test -e "$(DESTDIR)$(swanctldir)" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)" + test -e "$(DESTDIR)$(swanctldir)/swanctl.conf" || $(INSTALL) -m 640 $(srcdir)/swanctl.conf $(DESTDIR)$(swanctldir)/swanctl.conf || true From 5fdba04312988f31977232684386e47c3880dcfc Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 17 Apr 2014 18:59:42 +0200 Subject: [PATCH 29/38] swanctl: Convert swanctl.conf to an options file and generate config --- src/swanctl/.gitignore | 1 + src/swanctl/Makefile.am | 10 ++- src/swanctl/swanctl.conf | 125 -------------------------- src/swanctl/swanctl.opt | 188 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 126 deletions(-) delete mode 100644 src/swanctl/swanctl.conf create mode 100644 src/swanctl/swanctl.opt diff --git a/src/swanctl/.gitignore b/src/swanctl/.gitignore index 1db645ba7..a37446ed5 100644 --- a/src/swanctl/.gitignore +++ b/src/swanctl/.gitignore @@ -1 +1,2 @@ swanctl +swanctl.conf diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 4f0c1c2e6..3ed47fe84 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -29,7 +29,15 @@ AM_CPPFLAGS = \ -DSWANCTLDIR=\""${swanctldir}\"" \ -DPLUGINS=\""${s_plugins}\"" -EXTRA_DIST = swanctl.conf +BUILT_SOURCES = swanctl.conf +EXTRA_DIST = swanctl.opt swanctl.conf + +.opt.conf: + $(AM_V_GEN) \ + $(PYTHON) $(top_srcdir)/conf/format-options.py -f conf $< > $(srcdir)/$@ + +maintainer-clean-local: + cd $(srcdir) && rm -f swanctl.conf install-data-local: swanctl.conf test -e "$(DESTDIR)$(swanctldir)" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)" diff --git a/src/swanctl/swanctl.conf b/src/swanctl/swanctl.conf deleted file mode 100644 index f43d1d49b..000000000 --- a/src/swanctl/swanctl.conf +++ /dev/null @@ -1,125 +0,0 @@ -connections { - -# # an IKE configuration named conn1 -# conn1 { -# # IKE version to use -# version = 2 -# # list of acceptable local addresses/subnets -# local_addrs = 0.0.0.0 -# # peer address, additional addresses/subnets as responder -# remote_addrs = 192.168.5.1 -# # local UPD port for IKE -# local_port = 500 -# # remote UDP port for IKE -# remote_port = 500 -# # Proposals for IKE, "default" is the default proposal -# proposals = aes128gcm16-prfsha256-modp2048, default -# # virtual IPs to request, such as 0.0.0.0 or :: -# vips = -# # IKEv1 aggressive mode -# aggressive = no -# # use of pull/push in IKEv1 mode config -# pull = yes -# # enforce UDP encapsulation by faking NAT-D payloads -# encap = no -# # enable IKEv2 MOBIKE -# mobike = yes -# # interval of liveness checks -# dpd_delay = 10s -# # timeout for DPD checks (IKEV1 only) -# dpd_timeout = 30s -# # use IKEv1 UDP packet fragmentation -# fragmentation = force -# # send certificate requests -# send_certreq = yes -# # send certificate payloads -# send_cert = ifasked -# # number of retransmission sequences to do before givin up -# keyingtries = 0 -# # uniquness policy, never|no|keep|replace| -# unique = no -# # time to schedule IKE reauthentication -# reauth_time = 3h -# # time to schedule IKE rekeying -# rekey_time = 2h -# # hard IKE_SA lifetime if rekey/reauth does not complete -# over_time = 10m -# # range of random time to subtract from rekey/rauth times -# rand_time = 10m -# -# # local authentication, first round -# local { -# # additional certificates to load -# certs = a.pem, xy.der -# # authentication to perform locally -# auth = pubkey -# # IKE identity for local -# id = win@strongswan.org -# # Client EAP-Identity to use -# eap_id = moon -# # Server side EAP identity to use, EAP-TTLS etc. -# aaa_identity = srv -# # IKEv1 XAuth username -# xauth_id = moon -# } -# # remote authentication, first round -# remote { -# # IKE identity for peer -# id = %any -# # list of acceptable peer certificates -# certs = client.pem -# # list of acceptable CA certificates -# cacert = ca.der -# # revocation policy, strict|ifuri -# revocation = ifuri -# # authentication to expect from remote -# auth = pubkey -# } -# children { -# # First CHILD_SA configuration -# child1 { -# # AH proposals to offer -# ah_proposals = default -# # ESP proposals to offer -# esp_proposals = aes128gcm16-modp2048, default -# # local subnets to tunnel -# local_ts = 192.168.3.0/24 -# # remote subnets to tunnel -# remote_ts = 192.168.1.0/24 -# # updown script to invoke -# updown = path-to-script -# # hostaccess variable to pass to updown -# hostaccess = yes -# # IPsec mode, tunnel|transport|pass|drop -# mode = tunnel -# # action to perform on DPD timeout -# dpd_action = restart -# # enable IPComp -# ipcomp = no -# # inactivity timeout before closing CHILD_SA -# inactivity = 2m -# # fixed reqid to use for this CHILD_SA -# reqid = 5 -# # Netfilter mark for input traffic -# mark_in = 1 -# # Netfilter mark for output traffic -# mark_out = 5/0xffffffff -# # Traffic Flow Confidentiality padding -# tfc_padding = 1500 -# } -# } -# } - -} - -secrets { -# eap-tester { -# id = tester -# secret = test -# } -# ike-moon { -# id-local = sun.strongswan.org -# id-remote = mon.strongswan.org -# secret = 0x12345678901234 -# } -} diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt new file mode 100644 index 000000000..1e3adb2a6 --- /dev/null +++ b/src/swanctl/swanctl.opt @@ -0,0 +1,188 @@ +connections.conn1 { # } + An IKE configuration named conn1 + +connections.conn1.version = 2 + IKE version to use + +connections.conn1.local_addrs = 0.0.0.0 + List of acceptable local addresses/subnets + +connections.conn1.remote_addrs = 192.168.5.1 + Peer address, additional addresses/subnets as responder + +connections.conn1.local_port = 500 + Local UPD port for IKE + +connections.conn1.remote_port = 500 + Remote UDP port for IKE + +connections.conn1.proposals = aes128gcm16-prfsha256-modp2048, default + Proposals for IKE, "default" is the default proposal + +connections.conn1.vips = + Virtual IPs to request, such as 0.0.0.0 or :: + +connections.conn1.aggressive = no + IKEv1 aggressive mode + +connections.conn1.pull = yes + Use of pull/push in IKEv1 mode config + +connections.conn1.encap = no + Enforce UDP encapsulation by faking NAT-D payloads + +connections.conn1.mobike = yes + Enable IKEv2 MOBIKE + +connections.conn1.dpd_delay = 10s + Interval of liveness checks + +connections.conn1.dpd_timeout = 30s + Timeout for DPD checks (IKEV1 only) + +connections.conn1.fragmentation = force + Use IKEv1 UDP packet fragmentation + +connections.conn1.send_certreq = yes + Send certificate requests + +connections.conn1.send_cert = ifasked + Send certificate payloads + +connections.conn1.keyingtries = 0 + Number of retransmission sequences to do before givin up + +connections.conn1.unique = no + Uniquness policy, never|no|keep|replace| + +connections.conn1.reauth_time = 3h + Time to schedule IKE reauthentication + +connections.conn1.rekey_time = 2h + Time to schedule IKE rekeying + +connections.conn1.over_time = 10m + Hard IKE_SA lifetime if rekey/reauth does not complete + +connections.conn1.rand_time = 10m + Range of random time to subtract from rekey/rauth times + +connections.conn1.pools = pool1 + Hand out addresses and attributes from pool1 as responder + +connections.conn1.vips = 0.0.0.0 + Request a virtual IP as initiator + +connections.conn1.local {} + Local authentication, first round + +connections.conn1.local.certs = a.pem, xy.der + Additional certificates to load + +connections.conn1.local.auth = pubkey + Authentication to perform locally + +connections.conn1.local.id = win@strongswan.org + IKE identity for local + +connections.conn1.local.eap_id = moon + Client EAP-Identity to use + +connections.conn1.local.aaa_identity = srv + Server side EAP identity to use, EAP-TTLS etc. + +connections.conn1.local.xauth_id = moon + IKEv1 XAuth username + +connections.conn1.remote {} + Remote authentication, first round + +connections.conn1.remote.id = %any + IKE identity for peer + +connections.conn1.remote.certs = client.pem + List of acceptable peer certificates + +connections.conn1.remote.cacert = ca.der + List of acceptable CA certificates + +connections.conn1.remote.revocation = ifuri + Revocation policy, strict|ifuri + +connections.conn1.remote.auth = pubkey + Authentication to expect from remote + +connections.conn1.children.child1 {} + First CHILD_SA configuration + +connections.conn1.children.child1.ah_proposals = default + AH proposals to offer + +connections.conn1.children.child1.esp_proposals = aes128gcm16-modp2048, default + ESP proposals to offer + +connections.conn1.children.child1.local_ts = 192.168.3.0/24 + Local subnets to tunnel + +connections.conn1.children.child1.remote_ts = 192.168.1.0/24 + Remote subnets to tunnel + +connections.conn1.children.child1.updown = path-to-script + Updown script to invoke + +connections.conn1.children.child1.hostaccess = yes + Hostaccess variable to pass to updown + +connections.conn1.children.child1.mode = tunnel + IPsec mode, tunnel|transport|pass|drop + +connections.conn1.children.child1.dpd_action = restart + Action to perform on DPD timeout + +connections.conn1.children.child1.ipcomp = no + Enable IPComp + +connections.conn1.children.child1.inactivity = 2m + Inactivity timeout before closing CHILD_SA + +connections.conn1.children.child1.reqid = 5 + Fixed reqid to use for this CHILD_SA + +connections.conn1.children.child1.mark_in = 1 + Netfilter mark for input traffic + +connections.conn1.children.child1.mark_out = 5/0xffffffff + Netfilter mark for output traffic + +connections.conn1.children.child1.tfc_padding = 1500 + Traffic Flow Confidentiality padding + +secrets.eap1 { # } + EAP secret section + +secrets.eap1.secret = testpassword + Password for EAP secret + +secrets.eap1.id = tester + User EAP secret belongs to + +secrets.ike-moon { # } + IKE secret for moon + +secrets.ike-moon.secret = 0x12345678 + IKE shared secret for moon + +secrets.ike-moon.id-local = sun.strongswan.org + First identity secret belongs to + +secrets.ike-moon.id-remote = moon.strongswan.org + Second identity secret belongs to + +pools.poolx { # } + Section defining an address pool + +pools.poolx.addrs = 10.1.2.0/24 + Define addresses for this pool + +pools.poolx.dns = 10.1.1.1, 10.1.2.1 + Define DNS server addresses associated to pool From e20deeca77798f6f25f78f8b5f25d0c1321f4031 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 17 Apr 2014 19:06:34 +0200 Subject: [PATCH 30/38] conf: Properly propagate whether a section is commented or not --- conf/format-options.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/conf/format-options.py b/conf/format-options.py index fc6e6e1fd..e591f37cb 100755 --- a/conf/format-options.py +++ b/conf/format-options.py @@ -241,15 +241,16 @@ class ConfFormatter: def __print_section(self, section, indent, commented): """Print a section with all options""" - comment = "# " if commented or section.commented else "" + commented = commented or section.commented + comment = "# " if commented else "" self.__print_description(section, indent) print '{0}{1}{2} {{'.format(self.__indent * indent, comment, section.name) print for o in sorted(section.options, key=attrgetter('section')): if o.section: - self.__print_section(o, indent + 1, section.commented) + self.__print_section(o, indent + 1, commented) else: - self.__print_option(o, indent + 1, section.commented) + self.__print_option(o, indent + 1, commented) print '{0}{1}}}'.format(self.__indent * indent, comment) print From 6a461f0852f4a918095a276312dcd45ac107c8f8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 17 Apr 2014 19:15:10 +0200 Subject: [PATCH 31/38] swanctl: Generate man page snippet with config options --- src/swanctl/Makefile.am | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 3ed47fe84..f899c97f4 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -29,15 +29,19 @@ AM_CPPFLAGS = \ -DSWANCTLDIR=\""${swanctldir}\"" \ -DPLUGINS=\""${s_plugins}\"" -BUILT_SOURCES = swanctl.conf -EXTRA_DIST = swanctl.opt swanctl.conf +BUILT_SOURCES = swanctl.conf swanctl.conf.5.main +EXTRA_DIST = swanctl.opt swanctl.conf swanctl.conf.5.main .opt.conf: $(AM_V_GEN) \ $(PYTHON) $(top_srcdir)/conf/format-options.py -f conf $< > $(srcdir)/$@ +swanctl.conf.5.main: swanctl.opt + $(AM_V_GEN) \ + $(PYTHON) $(top_srcdir)/conf/format-options.py -f man $< > $(srcdir)/$@ + maintainer-clean-local: - cd $(srcdir) && rm -f swanctl.conf + cd $(srcdir) && rm -f swanctl.conf swanctl.conf.5.main install-data-local: swanctl.conf test -e "$(DESTDIR)$(swanctldir)" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)" From b18191ba0f7a38c8f89fb98248ca6796f5b5b0d7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 17 Apr 2014 19:23:48 +0200 Subject: [PATCH 32/38] swanctl: Generate swanctl.conf(5) man page --- configure.ac | 2 ++ src/swanctl/.gitignore | 4 ++++ src/swanctl/Makefile.am | 8 ++++++++ src/swanctl/swanctl.conf.5.head.in | 12 ++++++++++++ src/swanctl/swanctl.conf.5.tail.in | 10 ++++++++++ 5 files changed, 36 insertions(+) create mode 100644 src/swanctl/swanctl.conf.5.head.in create mode 100644 src/swanctl/swanctl.conf.5.tail.in diff --git a/configure.ac b/configure.ac index 6c056cff7..26065b15d 100644 --- a/configure.ac +++ b/configure.ac @@ -1639,6 +1639,8 @@ AC_CONFIG_FILES([ src/pki/man/pki---signcrl.1 src/pki/man/pki---acert.1 src/pki/man/pki---verify.1 + src/swanctl/swanctl.conf.5.head + src/swanctl/swanctl.conf.5.tail ]) AC_OUTPUT diff --git a/src/swanctl/.gitignore b/src/swanctl/.gitignore index a37446ed5..b92b5029c 100644 --- a/src/swanctl/.gitignore +++ b/src/swanctl/.gitignore @@ -1,2 +1,6 @@ swanctl swanctl.conf +swanctl.conf.5 +swanctl.conf.5.main +swanctl.conf.5.head +swanctl.conf.5.tail \ No newline at end of file diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index f899c97f4..a232487aa 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -29,8 +29,12 @@ AM_CPPFLAGS = \ -DSWANCTLDIR=\""${swanctldir}\"" \ -DPLUGINS=\""${s_plugins}\"" +man_MANS = \ + swanctl.conf.5 + BUILT_SOURCES = swanctl.conf swanctl.conf.5.main EXTRA_DIST = swanctl.opt swanctl.conf swanctl.conf.5.main +CLEANFILES = $(man_MANS) .opt.conf: $(AM_V_GEN) \ @@ -40,6 +44,10 @@ swanctl.conf.5.main: swanctl.opt $(AM_V_GEN) \ $(PYTHON) $(top_srcdir)/conf/format-options.py -f man $< > $(srcdir)/$@ +swanctl.conf.5: swanctl.conf.5.head swanctl.conf.5.main swanctl.conf.5.tail + $(AM_V_GEN) \ + cat swanctl.conf.5.head $(srcdir)/swanctl.conf.5.main swanctl.conf.5.tail > $@ + maintainer-clean-local: cd $(srcdir) && rm -f swanctl.conf swanctl.conf.5.main diff --git a/src/swanctl/swanctl.conf.5.head.in b/src/swanctl/swanctl.conf.5.head.in new file mode 100644 index 000000000..070c858be --- /dev/null +++ b/src/swanctl/swanctl.conf.5.head.in @@ -0,0 +1,12 @@ +.TH SWANCTL.CONF 5 "" "@PACKAGE_VERSION@" "strongSwan" +.SH NAME +swanctl.conf \- swanctl configuration file +.SH DESCRIPTION +Bla bla + +For a description of the syntax refer to +.BR strongswan.conf (5). + +.SH SETTINGS +The following settings can be used to configure IKE and CHILD SAs and +credentials. diff --git a/src/swanctl/swanctl.conf.5.tail.in b/src/swanctl/swanctl.conf.5.tail.in new file mode 100644 index 000000000..4d24608da --- /dev/null +++ b/src/swanctl/swanctl.conf.5.tail.in @@ -0,0 +1,10 @@ +.SH FILES +. +.nf +.na +/etc/swanctl/swanctl.conf configuration file +.ad +.fi +. +.SH SEE ALSO +.BR swanctl (8) From 85d26e0c875e222a769a4c971816f342648704df Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 28 Apr 2014 16:57:22 +0200 Subject: [PATCH 33/38] swanctl: Add a swanctl command overview manpage --- configure.ac | 1 + src/swanctl/.gitignore | 1 + src/swanctl/Makefile.am | 1 + src/swanctl/swanctl.8.in | 83 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 src/swanctl/swanctl.8.in diff --git a/configure.ac b/configure.ac index 26065b15d..966b9d002 100644 --- a/configure.ac +++ b/configure.ac @@ -1639,6 +1639,7 @@ AC_CONFIG_FILES([ src/pki/man/pki---signcrl.1 src/pki/man/pki---acert.1 src/pki/man/pki---verify.1 + src/swanctl/swanctl.8 src/swanctl/swanctl.conf.5.head src/swanctl/swanctl.conf.5.tail ]) diff --git a/src/swanctl/.gitignore b/src/swanctl/.gitignore index b92b5029c..11c04cb46 100644 --- a/src/swanctl/.gitignore +++ b/src/swanctl/.gitignore @@ -1,4 +1,5 @@ swanctl +swanctl.8 swanctl.conf swanctl.conf.5 swanctl.conf.5.main diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index a232487aa..e5b53a5d1 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -30,6 +30,7 @@ AM_CPPFLAGS = \ -DPLUGINS=\""${s_plugins}\"" man_MANS = \ + swanctl.8 \ swanctl.conf.5 BUILT_SOURCES = swanctl.conf swanctl.conf.5.main diff --git a/src/swanctl/swanctl.8.in b/src/swanctl/swanctl.8.in new file mode 100644 index 000000000..d7abae67a --- /dev/null +++ b/src/swanctl/swanctl.8.in @@ -0,0 +1,83 @@ +.TH SWANCTL 8 "2014-04-28" "@PACKAGE_VERSION@" "strongSwan" +.SH NAME +swanctl \- strongSwan configuration, control and monitoring command line interface. +.SH SYNOPSIS +.SY "swanctl" +.I command +.RI [ option\~ .\|.\|.] +.YS +. +.SY "swanctl" +.B \-h +| +.B \-\-help +.YS +. +.SH DESCRIPTION +swanctl is a cross-platform command line utility to configure, control and +monitor the strongSwan IKE daemon. It is a replacement for the aging +.BR starter , +.B ipsec +and +.B stroke +tools. + +swanctl uses a configuration file called +.BR swanctl.conf (5) +to parse configurations and credentials. Private keys, certificates and other +PKI related credentials are read from specific directories. + +To communicate with the IKE daemon, swanctl uses the VICI protocol, the +Versatile IKE Configuration Interface. This stable interface is usable by +other tools and is often preferable than scripting swanctl and parsing its +output. + +.SH COMMANDS +.TP +.B "\-i, \-\-initiate" +initiate a connection +.TP +.B "\-t, \-\-terminate" +\-\-terminate\fR +terminate a connection +.TP +.B "\-p, \-\-install" +install a trap or shunt policy +.TP +.B "\-u, \-\-uninstall" +uninstall a trap or shunt policy +.TP +.B "\-l, \-\-list\-sas" +list currently active IKE_SAs +.TP +.B "\-P, \-\-list\-pols" +list currently installed policies +.TP +.B "\-L, \-\-list\-conns" +list loaded configurations +.TP +.B "\-x, \-\-list\-certs" +list stored certificates +.TP +.B "\-A, \-\-list\-pools" +list loaded pool configurations +.TP +.B "\-c, \-\-load\-conns" +(re\-)load connection configuration +.TP +.B "\-s, \-\-load\-creds" +(re\-)load credentials +.TP +.B "\-a, \-\-load\-pools" +(re\-)load pool configuration +.TP +.B "\-T, \-\-log" +trace logging output +.TP +.B "\-v, \-\-version" +show daemon version information +.TP +.B "\-h, \-\-help" +show usage information +.SH SEE ALSO +.BR swanctl.conf (5) From ae98a39e71cf3912ff5fdbb6a837556cfeb9fe45 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 29 Apr 2014 12:13:33 +0200 Subject: [PATCH 34/38] conf: Add a format-options --nosort option to keep order of sections as defined --- conf/format-options.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/conf/format-options.py b/conf/format-options.py index e591f37cb..7d67c9890 100755 --- a/conf/format-options.py +++ b/conf/format-options.py @@ -92,8 +92,9 @@ class ConfigOption: class Parser: """Parses one or more files of configuration options""" - def __init__(self): + def __init__(self, sort = True): self.options = [] + self.sort = sort def parse(self, file): """Parses the given file and adds all options to the internal store""" @@ -145,7 +146,8 @@ class Parser: found.adopt(option) else: parent.options.append(option) - parent.options.sort() + if self.sort: + parent.options.sort() def __get_option(self, parts, create = False): """Searches/Creates the option (section) based on a list of section names""" @@ -160,7 +162,8 @@ class Parser: break option = ConfigOption(fullname, section = True) options.append(option) - options.sort() + if self.sort: + options.sort() options = option.options return option @@ -310,9 +313,12 @@ options.add_option("-f", "--format", dest="format", type="choice", choices=["con options.add_option("-r", "--root", dest="root", metavar="NAME", help="root section of which options are printed, " "if not found everything is printed") +options.add_option("-n", "--nosort", action="store_false", dest="sort", + default=True, help="do not sort sections alphabetically") + (opts, args) = options.parse_args() -parser = Parser() +parser = Parser(opts.sort) if len(args): for filename in args: try: From d909e5191843c1edd98188f4c7dc4238b9ea61f4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 29 Apr 2014 12:15:06 +0200 Subject: [PATCH 35/38] swanctl: Keep swanctl.conf man/template section order as defined --- src/swanctl/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index e5b53a5d1..fcc4d159f 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -39,11 +39,11 @@ CLEANFILES = $(man_MANS) .opt.conf: $(AM_V_GEN) \ - $(PYTHON) $(top_srcdir)/conf/format-options.py -f conf $< > $(srcdir)/$@ + $(PYTHON) $(top_srcdir)/conf/format-options.py -n -f conf $< > $(srcdir)/$@ swanctl.conf.5.main: swanctl.opt $(AM_V_GEN) \ - $(PYTHON) $(top_srcdir)/conf/format-options.py -f man $< > $(srcdir)/$@ + $(PYTHON) $(top_srcdir)/conf/format-options.py -n -f man $< > $(srcdir)/$@ swanctl.conf.5: swanctl.conf.5.head swanctl.conf.5.main swanctl.conf.5.tail $(AM_V_GEN) \ From 2230f18358e77d58839772e92d77eb19f2c2c6f0 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 28 Apr 2014 16:18:24 +0200 Subject: [PATCH 36/38] swanctl: Document most swanctl.conf options in manpage --- src/swanctl/swanctl.conf.5.head.in | 20 +- src/swanctl/swanctl.opt | 803 ++++++++++++++++++++++++----- 2 files changed, 693 insertions(+), 130 deletions(-) diff --git a/src/swanctl/swanctl.conf.5.head.in b/src/swanctl/swanctl.conf.5.head.in index 070c858be..84f734eb9 100644 --- a/src/swanctl/swanctl.conf.5.head.in +++ b/src/swanctl/swanctl.conf.5.head.in @@ -2,11 +2,23 @@ .SH NAME swanctl.conf \- swanctl configuration file .SH DESCRIPTION -Bla bla +swanctl.conf is the configuration file used by the +.BR swanctl (8) +tool to load configurations and credentials into the strongSwan IKE daemon. -For a description of the syntax refer to +For a description of the basic file syntax refer to .BR strongswan.conf (5). +.SH TIME FORMATS +For all options that define a time, the time is specified in seconds. The +.RI "" "s" "," +.RI "" "m" "," +.RI "" "h" "" +and +.RI "" "d" "" +suffixes explicitly define the units for seconds, minutes, hours and days, +respectively. + .SH SETTINGS -The following settings can be used to configure IKE and CHILD SAs and -credentials. +The following settings can be used to configure connections, credentials and +pools. diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 1e3adb2a6..73907b6ce 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -1,188 +1,739 @@ -connections.conn1 { # } - An IKE configuration named conn1 +connections { # } + Section defining IKE connection configurations. -connections.conn1.version = 2 - IKE version to use + Section defining IKE connection configurations. -connections.conn1.local_addrs = 0.0.0.0 - List of acceptable local addresses/subnets + The connections section defines IKE connection configurations, each in + its own subsections. In the keyword description below, the connection + is named __, but an arbitrary yet unique connection name can be + chosen for each connection subsection. -connections.conn1.remote_addrs = 192.168.5.1 - Peer address, additional addresses/subnets as responder +connections. { # } + Section for an IKE connection named . -connections.conn1.local_port = 500 - Local UPD port for IKE +connections..version = 0 + IKE major version to use for connection. -connections.conn1.remote_port = 500 - Remote UDP port for IKE + IKE major version to use for connection. _1_ uses IKEv1 aka ISAKMP, _2_ + uses IKEv2. A connection using the default of _0_ accepts both IKEv1 + and IKEv2 as responder, and initiates the connection actively with IKEv2. -connections.conn1.proposals = aes128gcm16-prfsha256-modp2048, default - Proposals for IKE, "default" is the default proposal +connections..local_addrs = %any + Local address(es) to use for IKE communication, comma separated. -connections.conn1.vips = - Virtual IPs to request, such as 0.0.0.0 or :: + Local address(es) to use for IKE communication, comma separated. Takes + single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges. -connections.conn1.aggressive = no - IKEv1 aggressive mode + As initiator, the first non-range/non-subnet is used to initiate the + connection from. As responder, the local destination address must match at + least to one of the specified addresses, subnets or ranges. -connections.conn1.pull = yes - Use of pull/push in IKEv1 mode config +connections..remote_addrs = %any + Remote address(es) to use for IKE communication, comma separated. -connections.conn1.encap = no - Enforce UDP encapsulation by faking NAT-D payloads + Remote address(es) to use for IKE communication, comma separated. Takes + single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges. -connections.conn1.mobike = yes - Enable IKEv2 MOBIKE + As initiator, the first non-range/non-subnet is used to initiate the + connection to. As responder, the initiator source address must match at + least to one of the specified addresses, subnets or ranges. -connections.conn1.dpd_delay = 10s - Interval of liveness checks + To initiate a connection, at least one specific address or DNS name must + be specified. -connections.conn1.dpd_timeout = 30s - Timeout for DPD checks (IKEV1 only) +connections..local_port = 500 + Local UPD port for IKE communication. -connections.conn1.fragmentation = force - Use IKEv1 UDP packet fragmentation + Local UPD port for IKE communication. By default the port of the socket + backend is used, which is usually _500_. If port _500_ is used, automatic + IKE port floating to port 4500 is used to work around NAT issues. -connections.conn1.send_certreq = yes - Send certificate requests + Using a non-default local IKE port requires support from the socket backend + in use (socket-dynamic). -connections.conn1.send_cert = ifasked - Send certificate payloads +connections..remote_port = 500 + Remote UDP port for IKE communication. -connections.conn1.keyingtries = 0 - Number of retransmission sequences to do before givin up + Remote UPD port for IKE communication. If the default of port _500_ is used, + automatic IKE port floating to port 4500 is used to work around NAT issues. -connections.conn1.unique = no - Uniquness policy, never|no|keep|replace| +connections..proposals = default + Comma separated proposals to accept for IKE. -connections.conn1.reauth_time = 3h - Time to schedule IKE reauthentication + A proposal is a set of algorithms. For non-AEAD algorithms, this includes + for IKE an encryption algorithm, an integrity algorithm, a pseudo random + function and a Diffie-Hellman group. For AEAD algorithms, instead of + encryption and integrity algorithms, a combined algorithm is used. -connections.conn1.rekey_time = 2h - Time to schedule IKE rekeying + In IKEv2, multiple algorithms of the same kind can be specified in a single + proposal, from which one gets selected. In IKEv1, only one algorithm per + kind is allowed per proposal, more algorithms get implicitly stripped. Use + multiple proposals to offer different algorithms combinations in IKEv1. -connections.conn1.over_time = 10m - Hard IKE_SA lifetime if rekey/reauth does not complete + Algorithm keywords get separated using dashes. Multiple proposals may be + separated by commas. The special value _default_ forms a default proposal + of supported algorithms considered safe, and is usually a good choice + for interoperability. -connections.conn1.rand_time = 10m - Range of random time to subtract from rekey/rauth times +connections..vips = + Virtual IPs to request in configuration payload / Mode Config. -connections.conn1.pools = pool1 - Hand out addresses and attributes from pool1 as responder + Comma separated list of virtual IPs to request in IKEv2 configuration + payloads or IKEv1 Mode Config. The wildcard addresses _0.0.0.0_ and _::_ + request an arbitrary address, specific addresses may be defined. The + responder may return a different address, though, or none at all. -connections.conn1.vips = 0.0.0.0 - Request a virtual IP as initiator +connections..aggressive = no + Use Aggressive Mode in IKEv1. -connections.conn1.local {} - Local authentication, first round + Enables Aggressive Mode instead of Main Mode with Identity Protection. + Aggressive Mode is considered less secure, because the ID and HASH + payloads are exchanged unprotected. This allows a passive attacker to + snoop peer identities, and even worse, start dictionary attacks on the + Preshared Key. -connections.conn1.local.certs = a.pem, xy.der - Additional certificates to load +connections..pull = yes + Set the Mode Config mode to use. -connections.conn1.local.auth = pubkey - Authentication to perform locally + If the default of _yes_ is used, Mode Config works in pull mode, where + the initiator actively requests a virtual IP. With _no_, push mode is used, + where the responder pushes down a virtual IP to the initiating peer. -connections.conn1.local.id = win@strongswan.org - IKE identity for local + Push mode is currently supported for IKEv1, but not in IKEv2. It is used + by a few implementations only, pull mode is recommended. -connections.conn1.local.eap_id = moon - Client EAP-Identity to use +connections..encap = no + Enforce UDP encapsulation by faking NAT-D payloads. -connections.conn1.local.aaa_identity = srv - Server side EAP identity to use, EAP-TTLS etc. + To enforce UDP encapsulation of ESP packets, the IKE daemon can fake the + NAT detection payloads. This makes the peer believe that NAT takes + place on the path, forcing it to encapsulate ESP packets in UDP. -connections.conn1.local.xauth_id = moon - IKEv1 XAuth username + Usually this is not required, but it can help to work around connectivity + issues with too restrictive intermediary firewalls. -connections.conn1.remote {} - Remote authentication, first round +connections..mobike = yes + Enables MOBIKE on IKEv2 connections. -connections.conn1.remote.id = %any - IKE identity for peer + Enables MOBIKE on IKEv2 connections. MOBIKE is enabled by default on IKEv2 + connections, and allows mobility of clients and multi-homing on servers by + migrating active IPsec tunnels. -connections.conn1.remote.certs = client.pem - List of acceptable peer certificates + Usually keeping MOBIKE enabled is unproblematic, as it is not used if the + peer does not indicate support for it. However, due to the design of MOBIKE, + IKEv2 always floats to port 4500 starting from the second exchange. Some + implementations don't like this behavior, hence it can be disabled. -connections.conn1.remote.cacert = ca.der - List of acceptable CA certificates +connections..dpd_delay = 0s + Interval of liveness checks (DPD). -connections.conn1.remote.revocation = ifuri - Revocation policy, strict|ifuri + Interval to check the liveness of a peer actively using IKEv2 INFORMATIONAL + exchanges or IKEv1 R_U_THERE messages. Active DPD checking is only enforced + if no IKE or ESP/AH packet has been received for the configured DPD delay. -connections.conn1.remote.auth = pubkey - Authentication to expect from remote +connections..dpd_timeout = 0s + Timeout for DPD checks (IKEV1 only). -connections.conn1.children.child1 {} - First CHILD_SA configuration + Charon by default uses the normal retransmission mechanism and timeouts to + check the liveness of a peer, as all messages are used for liveness + checking. For compatibility reasons, with IKEv1 a custom interval may be + specified; this option has no effect on connections using IKE2. -connections.conn1.children.child1.ah_proposals = default - AH proposals to offer +connections..fragmentation = no + Use IKEv1 UDP packet fragmentation (_yes_, _no_ or _force_). -connections.conn1.children.child1.esp_proposals = aes128gcm16-modp2048, default - ESP proposals to offer + The default of _no_ disables IKEv1 fragmentation mechanism, _yes_ enables + it if support has been indicated by the peer. _force_ enforces + fragmentation if required even before the peer had a chance to indicate + support for it. -connections.conn1.children.child1.local_ts = 192.168.3.0/24 - Local subnets to tunnel + IKE fragmentation is currently not supported with IKEv2. -connections.conn1.children.child1.remote_ts = 192.168.1.0/24 - Remote subnets to tunnel +connections..send_certreq = yes + Send certificate requests payloads (_yes_ or _no_). -connections.conn1.children.child1.updown = path-to-script - Updown script to invoke + Send certificate request payloads to offer trusted root CA certificates + to the peer. Certificate requests help the peer to choose an appropriate + certificate/private key for authentication and are enabled by default. -connections.conn1.children.child1.hostaccess = yes - Hostaccess variable to pass to updown + Disabling certificate requests can be useful if too many trusted root CA + certificates are installed, as each certificate request increases the size + of the initial IKE packets. -connections.conn1.children.child1.mode = tunnel - IPsec mode, tunnel|transport|pass|drop +connections..send_cert = ifasked + Send certificate payloads (_yes_, _no_ or _ifasked_). -connections.conn1.children.child1.dpd_action = restart - Action to perform on DPD timeout + Send certificate payloads when using certificate authentication. With the + default of _ifasked_ the daemon sends certificate payloads only if + certificate requests have been received. _no_ disables sending of + certificate payloads, _yes_ always sends certificate payloads whenever + certificate authentication is used. -connections.conn1.children.child1.ipcomp = no - Enable IPComp +connections..keyingtries = 1 + Number of retransmission sequences to perform during initial connect. -connections.conn1.children.child1.inactivity = 2m - Inactivity timeout before closing CHILD_SA + Number of retransmission sequences to perform during initial connect. + Instead of giving up initiation after the first retransmission sequence with + the default value of _1_, additional sequences may be started according to + the configured value. A value of _0_ initiates a new sequence until the + connection establishes or fails with a permanent error. -connections.conn1.children.child1.reqid = 5 - Fixed reqid to use for this CHILD_SA +connections..unique = no + Connection uniqueness policy (_never_, _no_, _keep_ or _replace_). -connections.conn1.children.child1.mark_in = 1 - Netfilter mark for input traffic + Connection uniqueness policy to enforce. To avoid multiple connections + from the same user, a uniqueness policy can be enforced. The value _never_ + does never enforce such a policy, even if a peer included INITIAL_CONTACT + notification messages, whereas _no_ replaces existing connections for the + same identity if a new one has the INITIAL_CONTACT notify. _keep_ rejects + new connection attempts if the same user already has an active connection, + _replace_ deletes any existing connection if a new one for the same user + gets established. -connections.conn1.children.child1.mark_out = 5/0xffffffff - Netfilter mark for output traffic + To compare connections for uniqueness, the remote IKE identity is used. If + EAP or XAuth authentication is involved, the EAP-Identity or XAuth username + is used to enforce the uniqueness policy instead. -connections.conn1.children.child1.tfc_padding = 1500 - Traffic Flow Confidentiality padding +connections..reauth_time = 0s + Time to schedule IKE reauthentication. -secrets.eap1 { # } - EAP secret section + Time to schedule IKE reauthentication. IKE reauthentication recreates the + IKE/ISAKMP SA from scratch and re-evaluates the credentials. In asymmetric + configurations (with EAP or configuration payloads) it might not be possible + to actively reauthenticate as responder. The IKEv2 reauthentication lifetime + negotiation can instruct the client to perform reauthentication. -secrets.eap1.secret = testpassword - Password for EAP secret + Reauthentication is disabled by default. Enabling it usually may lead + to small connection interruptions, as strongSwan uses a break-before-make + policy with IKEv2 to avoid any conflicts with associated tunnel resources. -secrets.eap1.id = tester - User EAP secret belongs to +connections..rekey_time = 4h + Time to schedule IKE rekeying. -secrets.ike-moon { # } - IKE secret for moon + IKE rekeying refreshes key material using a Diffie-Hellman exchange, but + does not re-check associated credentials. It is supported in IKEv2 only, + IKEv1 performs a reauthentication procedure instead. -secrets.ike-moon.secret = 0x12345678 - IKE shared secret for moon + With the default value IKE rekeying is scheduled every 4 hours, minus the + configured **rand_time**. -secrets.ike-moon.id-local = sun.strongswan.org - First identity secret belongs to +connections..over_time = 10% of rekey_time/reauth_time + Hard IKE_SA lifetime if rekey/reauth does not complete, as time. -secrets.ike-moon.id-remote = moon.strongswan.org - Second identity secret belongs to + Hard IKE_SA lifetime if rekey/reauth does not complete, as time. + To avoid having an IKE/ISAKMP kept alive if IKE reauthentication or rekeying + fails perpetually, a maximum hard lifetime may be specified. If the + IKE_SA fails to rekey or reauthenticate within the specified time, the + IKE_SA gets closed. -pools.poolx { # } - Section defining an address pool + In contrast to CHILD_SA rekeying, **over_time** is relative in time to the + **rekey_time** _and_ **reauth_time** values, as it applies to both. -pools.poolx.addrs = 10.1.2.0/24 - Define addresses for this pool + The default is 10% of the longer of **rekey_time** and **reauth_time**. -pools.poolx.dns = 10.1.1.1, 10.1.2.1 - Define DNS server addresses associated to pool +connections..rand_time = over_time + Range of random time to subtract from rekey/reauth times. + + Time range from which to choose a random value to subtract from + rekey/reauth times. To avoid having both peers initiating the rekey/reauth + procedure simultaneously, a random time gets subtracted from the + rekey/reauth times. + + The default is equal to the configured **over_time**. + +connections..pools = + Comma separated list of named IP pools. + + Comma separated list of named IP pools to allocate virtual IP addresses and + other configuration attributes from. Each name references a pool by name + from either the **pools** section or an external pool. + +connections..local {} + Section for a local authentication round. + + Section for a local authentication round. A local authentication round + defines the rules how authentication is performed for the local peer. + Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple Authentication + or IKEv1 XAuth. + + Each round is defined in a section having _local_ as prefix, and an optional + unique suffix. To define a single authentication round, the suffix may be + omitted. + +connections..local.certs = + Comma separated list of certificate candidates to use for authentication. + + Comma separated list of certificate candidates to use for authentication. + The certificates may use a relative path from the **swanctl** _x509_ + directory, or an absolute path. + + The certificate used for authentication is selected based on the received + certificate request payloads. If no appropriate CA can be located, the + first certificate is used. + +connections..local.auth = pubkey + Authentication to perform locally (_pubkey_, _psk_, _xauth[-backend]_ or + _eap[-method]_). + + Authentication to perform locally. _pubkey_ uses public key authentication + using a private key associated to a usable certificate. _psk_ uses + pre-shared key authentication. The IKEv1 specific _xauth_ is used for + XAuth or Hybrid authentication, while the IKEv2 specific _eap_ keyword + defines EAP authentication. + + For _xauth_, a specific backend name may be appended, separated by a dash. + The appropriate _xauth_ backend is selected to perform the XAuth exchange. + For traditional XAuth, the _xauth_ method is usually defined in the second + authentication round following an initial _pubkey_ (or _psk_) round. Using + _xauth_ in the first round performs Hybrid Mode client authentication. + + For _eap_, a specific EAP method name may be appended, separated by a dash. + An EAP module implementing the appropriate method is selected to perform + the EAP conversation. + +connections..local.id = + IKE identity to use for authentication round. + + IKE identity to use for authentication round. When using certificate + authentication, the IKE identity must be contained in the certificate, + either as subject or as subjectAltName. + +connections..local.eap_id = id + Client EAP-Identity to use in EAP-Identity exchange and the EAP method. + +connections..local.aaa_id = remote-id + Server side EAP-Identity to expect in the EAP method. + + Server side EAP-Identity to expect in the EAP method. Some EAP methods, such + as EAP-TLS, use an identity for the server to perform mutual authentication. + This identity may differ from the IKE identity, especially when EAP + authentication is delegated from the IKE responder to an AAA backend. + + For EAP-(T)TLS, this defines the identity for wich the server must provide + a certificate in the TLS exchange. + +connections..local.xauth_id = id + Client XAuth username used in the XAuth exchange. + +connections..remote {} + Section for a remote authentication round. + + Section for a remote authentication round. A remote authentication round + defines the constraints how the peers must authenticate to use this + connection. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple + Authentication or IKEv1 XAuth. + + Each round is defined in a section having _remote_ as prefix, and an + optional unique suffix. To define a single authentication round, the suffix + may be omitted. + +connections..remote.id = %any + IKE identity to expect for authentication round. + + IKE identity to expect for authentication round. When using certificate + authentication, the IKE identity must be contained in the certificate, + either as subject or as subjectAltName. + +connections..remote.groups = + Authorization group memberships to require. + + Comma separated authorization group memberships to require. The peer must + prove membership to at least one of the specified groups. Group membership + can be certified by different means, for example by appropriate Attribute + Certificates or by an AAA backend involved in the authentication. + +connections..remote.certs = + Comma separated list of certificate to accept for authentication. + + Comma separated list of certificates to accept for authentication. + The certificates may use a relative path from the **swanctl** _x509_ + directory, or an absolute path. + +connections..remote.cacert = + Comma separated list of CA certificates to accept for authentication. + + Comma separated list of CA certificates to accept for authentication. + The certificates may use a relative path from the **swanctl** _x509ca_ + directory, or an absolute path. + +connections..remote.revocation = relaxed + Certificate revocation policy, (_strict_, _ifuri_ or _relaxed_). + + Certificate revocation policy for CRL or OCSP revocation. + + A _strict_ revocation policy fails if no revocation information is + available, i.e. the certificate is not known to be unrevoked. + + _ifuri_ fails only if a CRL/OCSP URI is available, but certificate + revocation checking fails, i.e. there should be revocation information + available, but it could not be obtained. + + The default revocation policy _relaxed_ fails only if a certificate + is revoked, i.e. it is explicitly known that it is bad. + +connections..remote.auth = pubkey + Authentication to expect from remote (_pubkey_, _psk_, _xauth[-backend]_ or + _eap[-method]_). + + Authentication to expect from remote. See the **local** sections **auth** + keyword description about the details of supported mechanisms. + +connections..children. {} + CHILD_SA configuration sub-section. + + CHILD_SA configuration sub-section. Each connection definition may have + one or more sections in its _children_ subsection. The section name + defines the name of the CHILD_SA configuration, which must be unique within + the connection. + +connections..children..ah_proposals = + AH proposals to offer for the CHILD_SA. + + AH proposals to offer for the CHILD_SA. A proposal is a set of algorithms. + For AH, this includes an integrity algorithm and an optional Diffie-Hellman + group. If a DH group is specified, CHILD_SA/Quick Mode rekeying and initial + negotiation uses a separate Diffie-Hellman exchange using the specified + group. + + In IKEv2, multiple algorithms of the same kind can be specified in a single + proposal, from which one gets selected. In IKEv1, only one algorithm per + kind is allowed per proposal, more algorithms get implicitly stripped. Use + multiple proposals to offer different algorithms combinations in IKEv1. + + Algorithm keywords get separated using dashes. Multiple proposals may be + separated by commas. The special value _default_ forms a default proposal + of supported algorithms considered safe, and is usually a good choice + for interoperability. By default no AH proposals are included, instead ESP + is proposed. + +connections..children..esp_proposals = default + ESP proposals to offer for the CHILD_SA. + + ESP proposals to offer for the CHILD_SA. A proposal is a set of algorithms. + For ESP non-AEAD proposals, this includes an integrity algorithm, an + encryption algorithm, an optional Diffie-Hellman group and an optional + Extended Sequence Number Mode indicator. For AEAD proposals, a combined + mode algorithm is used instead of the separate encryption/integrity + algorithms. + + If a DH group is specified, CHILD_SA/Quick Mode rekeying and initial (non + IKE_AUTH piggybacked) negotiation uses a separate Diffie-Hellman exchange + using the specified group. Extended Sequence Number support may be indicated + with the _esn_ and _noesn_ values, both may be included to indicate support + for both modes. If omitted, _noesn_ is assumed. + + In IKEv2, multiple algorithms of the same kind can be specified in a single + proposal, from which one gets selected. In IKEv1, only one algorithm per + kind is allowed per proposal, more algorithms get implicitly stripped. Use + multiple proposals to offer different algorithms combinations in IKEv1. + + Algorithm keywords get separated using dashes. Multiple proposals may be + separated by commas. The special value _default_ forms a default proposal + of supported algorithms considered safe, and is usually a good choice + for interoperability. If no algorithms are specified for AH nor ESP, + the _default_ set of algorithms for ESP is included. + +connections..children..local_ts = dynamic + Local traffic selectors to include in CHILD_SA. + + Comma separated list of local traffic selectors to include in CHILD_SA. + Each selector is a CIDR subnet definition, followed by an optional + proto/port selector. The special value _dynamic_ may be used instead of a + subnet definition, which gets replaced by the tunnel outer address or the + virtual IP, if negotiated. This is the default. + + A protocol/port selector is surrounded by opening and closing square + brackets. Between these brackets, a numeric or **getservent**(3) protocol + name may be specified. After the optional protocol restriction, an optional + port restriction may be specified, separated by a slash. The port + restriction may be numeric, a **getservent**(3) service name, or the special + value _opaque_ for RFC 4301 OPAQUE selectors. Port ranges may be specified + as well, none of the kernel backends currently support port ranges, though. + + Unless the Unity extension is used, IKEv1 supports the first specified + selector only. IKEv1 uses very similar traffic selector narrowing as it is + supported in the IKEv2 protocol. + +connections..children..remote_ts = dynamic + Remote selectors to include in CHILD_SA. + + Comma separated list of remote selectors to include in CHILD_SA. See + **local_ts** for a description of the selector syntax. + +connections..children..rekey_time = 1h + Time to schedule CHILD_SA rekeying. + + Time to schedule CHILD_SA rekeying. CHILD_SA rekeying refreshes key + material, optionally using a Diffie-Hellman exchange if a group is + specified in the proposal. + + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of **rand_time** gets subtracted to form the effective soft + lifetime. + + By default CHILD_SA rekeying is scheduled every hour, minus **rand_time**. + +connections..children..life_time = rekey_time + 10% + Maximum lifetime before CHILD_SA gets closed, as time. + + Maximum lifetime before CHILD_SA gets closed. Usually this hard lifetime + is never reached, because the CHILD_SA gets rekeyed before. + If that fails for whatever reason, this limit closes the CHILD_SA. + + The default is 10% more than the **rekey_time**. + +connections..children..rand_time = life_time - rekey_time + Range of random time to subtract from **rekey_time**. + + Time range from which to choose a random value to subtract from + **rekey_time**. The default is the difference between **life_time** and + **rekey_time**. + +connections..children..rekey_bytes = 0 + Number of bytes processed before initiating CHILD_SA rekeying. + + Number of bytes processed before initiating CHILD_SA rekeying. CHILD_SA + rekeying refreshes key material, optionally using a Diffie-Hellman exchange + if a group is specified in the proposal. + + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of **rand_bytes** gets subtracted to form the effective soft + volume limit. + + Volume based CHILD_SA rekeying is disabled by default. + +connections..children..life_bytes = rekey_bytes + 10% + Maximum bytes processed before CHILD_SA gets closed. + + Maximum bytes processed before CHILD_SA gets closed. Usually this hard + volume limit is never reached, because the CHILD_SA gets rekeyed before. + If that fails for whatever reason, this limit closes the CHILD_SA. + + The default is 10% more than **rekey_bytes**. + +connections..children..rand_bytes = life_bytes - rekey_bytes + Range of random bytes to subtract from **rekey_bytes**. + + Byte range from which to choose a random value to subtract from + **rekey_bytes**. The default is the difference between **life_bytes** and + **rekey_bytes**. + +connections..children..rekey_packets = 0 + Number of packets processed before initiating CHILD_SA rekeying. + + Number of packets processed before initiating CHILD_SA rekeying. CHILD_SA + rekeying refreshes key material, optionally using a Diffie-Hellman exchange + if a group is specified in the proposal. + + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of **rand_packets** gets subtracted to form the effective soft + packet count limit. + + Packet count based CHILD_SA rekeying is disabled by default. + +connections..children..life_packets = rekey_packets + 10% + Maximum number of packets processed before CHILD_SA gets closed. + + Maximum number of packets processed before CHILD_SA gets closed. Usually + this hard packets limit is never reached, because the CHILD_SA gets rekeyed + before. If that fails for whatever reason, this limit closes the CHILD_SA. + + The default is 10% more than **rekey_bytes**. + +connections..children..rand_packets = life_packets - rekey_packets + Range of random packets to subtract from **packets_bytes**. + + Packet range from which to choose a random value to subtract from + **rekey_packets**. The default is the difference between **life_packets** + and **rekey_packets**. + +connections..children..updown = + Updown script to invoke on CHILD_SA up and down events. + +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 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 + addresses without the need to include them in each packet. + + Both _transport_ and _beet_ modes are subject to mode negotiation; _tunnel_ + mode is negotiated if the preferred mode is not available. + + _pass_ and _drop_ are used to install shunt policies, which explicitly + bypass the defined traffic from IPsec processing, or drop it, respectively. + +connections..children..dpd_action = clear + Action to perform on DPD timeout (_clear_, _trap_ or _restart_). + + Action to perform for this CHILD_SA on DPD timeout. The default _clear_ + closes the CHILD_SA and does not take further action. _trap_ installs + a trap policy, which will catch matching traffic and tries to re-negotiate + the tunnel on-demand. _restart_ immediately tries to re-negotiate the + CHILD_SA under a fresh IKE_SA. + +connections..children..ipcomp = no + Enable IPComp compression before encryption. + + Enable IPComp compression before encryption. If enabled, IKE tries to + negotiate IPComp compression to compress ESP payload data prior to + encryption. + +connections..children..inactivity = 0s + Timeout before closing CHILD_SA after inactivity. + + Timeout before closing CHILD_SA after inactivity. If no traffic has + been processed in either direction for the configured timeout, the CHILD_SA + gets closed due to inactivity. The default value of _0_ disables inactivity + checks. + +connections..children..reqid = 0 + Fixed reqid to use for this CHILD_SA. + + Fixed reqid to use for this CHILD_SA. This might be helpful in some + scenarios, but works only if each CHILD_SA configuration is instantiated + not more than once. The default of _0_ uses dynamic reqids, allocated + incrementally. + +connections..children..mark_in = 0/0x00000000 + Netfilter mark and mask for input traffic. + + Netfilter mark and mask for input traffic. On Linux Netfilter may apply + marks to each packet coming from a tunnel having that option set. The + mark may then be used by Netfilter to match rules. + + An additional mask may be appended to the mark, separated by _/_. The + default mask if omitted is 0xffffffff. + +connections..children..mark_out = 0/0x00000000 + Netfilter mark and mask for output traffic. + + Netfilter mark and mask for output traffic. On Linux Netfilter may require + marks on each packet to match a policy having that option set. This allows + Netfilter rules to select specific tunnels for outgoing traffic. + + An additional mask may be appended to the mark, separated by _/_. The + default mask if omitted is 0xffffffff. + +connections..children..tfc_padding = 0 + Traffic Flow Confidentiality padding. + + Pads ESP packets with additional data to have a consistent ESP packet size + for improved Traffic Flow Confidentiality. The padding defines the minimum + size of all ESP packets sent. + + The default value of 0 disables TFC padding, the special value _mtu_ adds + TFC padding to create a packet size equal to the Path Maximum Transfer Unit. + +connections..children..start_action = none + Action to perform after loading the configuration (_none_, _trap_, _start_). + + Action to perform after loading the configuration. The default of _none_ + loads the connection only, which then can be manually initiated or used as + a responder configuration. + + The value _trap_ installs a trap policy, which triggers the tunnel as soon + as matching traffic has been detected. The value _start_ initiates + the connection actively. + + When unloading or replacing a CHILD_SA configuration having a + **start_action** different from _none_, the inverse action is performed. + Configurations with _start_ get closed, while such with _trap_ get + uninstalled. + +connections..children..close_action = none + Action to perform after a CHILD_SA gets closed (_none_, _trap_, _start_). + + Action to perform after a CHILD_SA gets closed by the peer. The default of + _none_ does not take any action, _trap_ installs a trap policy for the + CHILD_SA. _start_ tries to re-create the CHILD_SA. + + **close_action** does not provide any guarantee that the CHILD_SA is kept + alive. It acts on explicit close messages only, but not on negotiation + failures. Use trap policies to reliably re-create failed CHILD_SAs. + +secrets { # } + Section defining secrets for IKE and EAP/XAuth authentication. + + Section defining secrets for IKE and EAP/XAuth authentication. The + **secrets** section takes sub-sections having a specific prefix which + defines the secret type. + +secrets.eap { # } + EAP secret section for a specific secret. + + EAP secret section for a specific secret. Each EAP secret is defined in + a unique section having the _eap_ prefix. EAP secrets are used for XAuth + authentication as well. + +secrets.xauth { # } + XAuth secret section for a specific secret. + + XAuth secret section for a specific secret. **xauth** is just an alias + for **eap**, secrets under both section prefixes are used for both EAP and + XAuth authentication. + +secrets.eap.secret = + Value of the EAP/XAuth secret. + + Value of the EAP/XAuth secret. It may either be an ASCII string, a hex + encoded string if it has a _0x_ prefix, or a Base64 encoded string if it + has a _0s_ prefix in its value. + +secrets.eap.id = + Identity the EAP/XAuth secret belongs to. + + Identity the EAP/XAuth 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. + + IKE preshared secret section for a specific secret. Each IKE PSK is defined + in a unique section having the _ike_ prefix. + +secrets.ike.secret = + Value of the IKE preshared secret. + + Value of the IKE preshared secret. It may either be an ASCII string, + a hex encoded string if it has a _0x_ prefix, or a Base64 encoded string if + it has a _0s_ prefix in its value. + +secrets.ike.id = + IKE identity the IKE preshared secret belongs to. + + IKE identity the IKE preshared secret belongs to. Multiple unique identities + may be specified, each having an _id_ prefix, if a secret is shared between + multiple peers. + +pools { # } + Section defining named pools. + + Section defining named pools. Named pools may be referenced by connections + with the **pools** option to assign virtual IPs and other configuration + attributes. + +pools. { # } + Section defining a single pool with a unique name. + +pools..addrs = + Subnet defining addresses allocated in pool. + + Subnet defining addresses allocated in pool. Accepts a single CIDR subnet + defining the pool to allocate addresses from. Pools must be unique and + non-overlapping. + +pools.. = + Comma separated list of additional attributes from type . + + Comma separated list of additional attributes of type ****. The + attribute type may be one of _dns_, _nbns_, _dhcp_, _netmask_, _server_, + _subnet_, _split_include_ and _split_exclude_ to define addresses or CIDR + subnets for the corresponding attribute types. Alternatively, **** can + be a numerical identifier, for which string attribute values are accepted + as well. From 92884b4683e6591a1799b9fb6de57a8b0affee7f Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 29 Apr 2014 16:03:44 +0200 Subject: [PATCH 37/38] swanctl: Install empty credential folders with appropriate permissions --- src/swanctl/Makefile.am | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index fcc4d159f..a262a2fa7 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -54,4 +54,12 @@ maintainer-clean-local: install-data-local: swanctl.conf test -e "$(DESTDIR)$(swanctldir)" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)" + test -e "$(DESTDIR)$(swanctldir)/x509" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)/x509" || true + test -e "$(DESTDIR)$(swanctldir)/x509ca" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)/x509ca" || true + test -e "$(DESTDIR)$(swanctldir)/x509aa" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)/x509aa" || true + test -e "$(DESTDIR)$(swanctldir)/x509crl" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)/x509crl" || true + test -e "$(DESTDIR)$(swanctldir)/x509ac" || $(INSTALL) -d "$(DESTDIR)$(swanctldir)/x509ac" || true + test -e "$(DESTDIR)$(swanctldir)/rsa" || $(INSTALL) -d -m 750 "$(DESTDIR)$(swanctldir)/rsa" || true + test -e "$(DESTDIR)$(swanctldir)/ecdsa" || $(INSTALL) -d -m 750 "$(DESTDIR)$(swanctldir)/ecdsa" || true + test -e "$(DESTDIR)$(swanctldir)/pkcs8" || $(INSTALL) -d -m 750 "$(DESTDIR)$(swanctldir)/pkcs8" || true test -e "$(DESTDIR)$(swanctldir)/swanctl.conf" || $(INSTALL) -m 640 $(srcdir)/swanctl.conf $(DESTDIR)$(swanctldir)/swanctl.conf || true From b1076bc8fd9fe41156afd832c8b1ec65e9cfcf8c Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 6 May 2014 10:56:07 +0200 Subject: [PATCH 38/38] swanctl: By default print local swanctl version with --version But add a --daemon option to query the IKE daemon for its version. --- src/swanctl/commands/version.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/swanctl/commands/version.c b/src/swanctl/commands/version.c index 36b7a6db4..c44004dee 100644 --- a/src/swanctl/commands/version.c +++ b/src/swanctl/commands/version.c @@ -22,7 +22,7 @@ static int version(vici_conn_t *conn) vici_req_t *req; vici_res_t *res; char *arg; - bool raw = FALSE; + bool raw = FALSE, daemon = FALSE;; while (TRUE) { @@ -33,6 +33,9 @@ static int version(vici_conn_t *conn) case 'r': raw = TRUE; continue; + case 'd': + daemon = TRUE; + continue; case EOF: break; default: @@ -41,6 +44,12 @@ static int version(vici_conn_t *conn) break; } + if (!daemon) + { + printf("strongSwan swanctl %s\n", VERSION); + return 0; + } + req = vici_begin("version"); res = vici_submit(req, conn); if (!res) @@ -71,10 +80,11 @@ static int version(vici_conn_t *conn) static void __attribute__ ((constructor))reg() { command_register((command_t) { - version, 'v', "version", "show daemon version information", + version, 'v', "version", "show version information", {"[--raw]"}, { {"help", 'h', 0, "show usage information"}, + {"daemon", 'd', 0, "query daemon version"}, {"raw", 'r', 0, "dump raw response message"}, } });