Merge branch 'swanctl'
Adds a swanctl command line tool to manage the IKE daemon charon using the VICI interface provided by the vici plugin.
This commit is contained in:
+14
-7
@@ -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
|
||||
|
||||
@@ -241,15 +244,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
|
||||
|
||||
@@ -309,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:
|
||||
|
||||
@@ -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])
|
||||
@@ -265,6 +266,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 +401,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 +1395,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 +1612,7 @@ AC_CONFIG_FILES([
|
||||
src/checksum/Makefile
|
||||
src/conftest/Makefile
|
||||
src/pt-tls-client/Makefile
|
||||
src/swanctl/Makefile
|
||||
scripts/Makefile
|
||||
testing/Makefile
|
||||
])
|
||||
@@ -1631,6 +1639,9 @@ 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
|
||||
])
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -76,6 +76,10 @@ if USE_TOOLS
|
||||
SUBDIRS += scepclient pki
|
||||
endif
|
||||
|
||||
if USE_SWANCTL
|
||||
SUBDIRS += swanctl
|
||||
endif
|
||||
|
||||
if USE_CONFTEST
|
||||
SUBDIRS += conftest
|
||||
endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
swanctl
|
||||
swanctl.8
|
||||
swanctl.conf
|
||||
swanctl.conf.5
|
||||
swanctl.conf.5.main
|
||||
swanctl.conf.5.head
|
||||
swanctl.conf.5.tail
|
||||
@@ -0,0 +1,65 @@
|
||||
sbin_PROGRAMS = swanctl
|
||||
|
||||
swanctl_SOURCES = \
|
||||
command.c command.h \
|
||||
commands/initiate.c \
|
||||
commands/terminate.c \
|
||||
commands/install.c \
|
||||
commands/list_sas.c \
|
||||
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 \
|
||||
commands/log.c \
|
||||
commands/version.c \
|
||||
swanctl.c swanctl.h
|
||||
|
||||
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 \
|
||||
-DSWANCTLDIR=\""${swanctldir}\"" \
|
||||
-DPLUGINS=\""${s_plugins}\""
|
||||
|
||||
man_MANS = \
|
||||
swanctl.8 \
|
||||
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) \
|
||||
$(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 -n -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
|
||||
|
||||
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
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <library.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/optionsfrom.h>
|
||||
|
||||
/**
|
||||
* 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; i < MAX_COMMANDS && 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; i < MAX_COMMANDS && 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; i < MAX_COMMANDS && 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);
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <libvici.h>
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* Maximum number of commands (+1).
|
||||
*/
|
||||
#define MAX_COMMANDS 15
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
|
||||
|
||||
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 <name> [--timeout <s>] [--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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
|
||||
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 <name> [--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 <name> [--raw]"},
|
||||
{
|
||||
{"help", 'h', 0, "show usage information"},
|
||||
{"child", 'c', 1, "CHILD_SA configuration to install"},
|
||||
{"raw", 'r', 0, "dump raw response message"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <asn1/asn1.h>
|
||||
#include <asn1/oid.h>
|
||||
#include <credentials/certificates/certificate.h>
|
||||
#include <credentials/certificates/x509.h>
|
||||
#include <credentials/certificates/crl.h>
|
||||
#include <credentials/certificates/ac.h>
|
||||
#include <selectors/traffic_selector.h>
|
||||
|
||||
#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 <dn/san>] [--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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
/**
|
||||
* 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(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, conn_list, 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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
/**
|
||||
* 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 <name>] [--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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
/**
|
||||
* 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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "swanctl.h"
|
||||
|
||||
/**
|
||||
* 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",
|
||||
"pools",
|
||||
"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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 if (is_file_list_key(key))
|
||||
{
|
||||
add_file_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;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("loaded connection '%s'\n", section);
|
||||
}
|
||||
vici_free_res(res);
|
||||
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, unloaded = 0;
|
||||
char *arg, *section;
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *conns;
|
||||
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(SWANCTL_CONF);
|
||||
if (!cfg)
|
||||
{
|
||||
fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
|
||||
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))
|
||||
{
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
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, %u unloaded\n", unloaded);
|
||||
return 0;
|
||||
}
|
||||
if (loaded == found)
|
||||
{
|
||||
printf("successfully loaded %u connections, %u unloaded\n",
|
||||
loaded, unloaded);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "loaded %u of %u connections, %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_conns, 'c', "load-conns", "(re-)load connection configuration",
|
||||
{"[--raw]"},
|
||||
{
|
||||
{"help", 'h', 0, "show usage information"},
|
||||
{"raw", 'r', 0, "dump raw response message"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "swanctl.h"
|
||||
|
||||
#include <credentials/sets/callback_cred.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;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("loaded %s certificate '%s'\n", type, dir);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("loaded %s key '%s'\n", type, dir);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single secret over VICI
|
||||
*/
|
||||
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 *key, *value, buf[128], *type = NULL;
|
||||
bool ret = TRUE;
|
||||
int i;
|
||||
char *types[] = {
|
||||
"eap",
|
||||
"xauth",
|
||||
"ike",
|
||||
};
|
||||
|
||||
for (i = 0; i < countof(types); i++)
|
||||
{
|
||||
if (strpfx(section, types[i]))
|
||||
{
|
||||
type = types[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!type)
|
||||
{
|
||||
fprintf(stderr, "ignoring unsupported secret '%s'\n", section);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else if (strcasepfx(value, "0s"))
|
||||
{
|
||||
data = chunk_from_base64(chunk_from_str(value + 2), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("loaded %s secret '%s'\n", type, section);
|
||||
}
|
||||
vici_free_res(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, noprompt = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
settings_t *cfg;
|
||||
char *arg, *section;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (command_getopt(&arg))
|
||||
{
|
||||
case 'h':
|
||||
return command_usage(NULL);
|
||||
case 'c':
|
||||
clear = TRUE;
|
||||
continue;
|
||||
case 'n':
|
||||
noprompt = 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);
|
||||
|
||||
load_keys(conn, raw, noprompt, "rsa", SWANCTL_RSADIR);
|
||||
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_secret(conn, cfg, section, raw);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
cfg->destroy(cfg);
|
||||
|
||||
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"},
|
||||
{"noprompt", 'n', 0, "do not prompt for passwords"},
|
||||
{"raw", 'r', 0, "dump raw response message"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
|
||||
|
||||
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 <name> | --ike <name | --child-id <id> | --ike-id <id>",
|
||||
"[--timeout <s>] [--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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
|
||||
static int version(vici_conn_t *conn)
|
||||
{
|
||||
vici_req_t *req;
|
||||
vici_res_t *res;
|
||||
char *arg;
|
||||
bool raw = FALSE, daemon = FALSE;;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (command_getopt(&arg))
|
||||
{
|
||||
case 'h':
|
||||
return command_usage(NULL);
|
||||
case 'r':
|
||||
raw = TRUE;
|
||||
continue;
|
||||
case 'd':
|
||||
daemon = TRUE;
|
||||
continue;
|
||||
case EOF:
|
||||
break;
|
||||
default:
|
||||
return command_usage("invalid --terminate option");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!daemon)
|
||||
{
|
||||
printf("strongSwan swanctl %s\n", VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 version information",
|
||||
{"[--raw]"},
|
||||
{
|
||||
{"help", 'h', 0, "show usage information"},
|
||||
{"daemon", 'd', 0, "query daemon version"},
|
||||
{"raw", 'r', 0, "dump raw response message"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
|
||||
#include <library.h>
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
.TH SWANCTL.CONF 5 "" "@PACKAGE_VERSION@" "strongSwan"
|
||||
.SH NAME
|
||||
swanctl.conf \- swanctl configuration file
|
||||
.SH DESCRIPTION
|
||||
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 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 connections, credentials and
|
||||
pools.
|
||||
@@ -0,0 +1,10 @@
|
||||
.SH FILES
|
||||
.
|
||||
.nf
|
||||
.na
|
||||
/etc/swanctl/swanctl.conf configuration file
|
||||
.ad
|
||||
.fi
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR swanctl (8)
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* 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_ @}*/
|
||||
@@ -0,0 +1,739 @@
|
||||
connections { # }
|
||||
Section defining IKE connection configurations.
|
||||
|
||||
Section defining IKE connection configurations.
|
||||
|
||||
The connections section defines IKE connection configurations, each in
|
||||
its own subsections. In the keyword description below, the connection
|
||||
is named _<conn>_, but an arbitrary yet unique connection name can be
|
||||
chosen for each connection subsection.
|
||||
|
||||
connections.<conn> { # }
|
||||
Section for an IKE connection named <conn>.
|
||||
|
||||
connections.<conn>.version = 0
|
||||
IKE major version to use for connection.
|
||||
|
||||
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.<conn>.local_addrs = %any
|
||||
Local address(es) to use for IKE communication, comma separated.
|
||||
|
||||
Local address(es) to use for IKE communication, comma separated. Takes
|
||||
single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges.
|
||||
|
||||
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.<conn>.remote_addrs = %any
|
||||
Remote address(es) to use for IKE communication, comma separated.
|
||||
|
||||
Remote address(es) to use for IKE communication, comma separated. Takes
|
||||
single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges.
|
||||
|
||||
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.
|
||||
|
||||
To initiate a connection, at least one specific address or DNS name must
|
||||
be specified.
|
||||
|
||||
connections.<conn>.local_port = 500
|
||||
Local UPD port for IKE communication.
|
||||
|
||||
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.
|
||||
|
||||
Using a non-default local IKE port requires support from the socket backend
|
||||
in use (socket-dynamic).
|
||||
|
||||
connections.<conn>.remote_port = 500
|
||||
Remote UDP port for IKE communication.
|
||||
|
||||
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.<conn>.proposals = default
|
||||
Comma separated proposals to accept for IKE.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
connections.<conn>.vips =
|
||||
Virtual IPs to request in configuration payload / Mode Config.
|
||||
|
||||
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.<conn>.aggressive = no
|
||||
Use Aggressive Mode in IKEv1.
|
||||
|
||||
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.<conn>.pull = yes
|
||||
Set the Mode Config mode to use.
|
||||
|
||||
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.
|
||||
|
||||
Push mode is currently supported for IKEv1, but not in IKEv2. It is used
|
||||
by a few implementations only, pull mode is recommended.
|
||||
|
||||
connections.<conn>.encap = no
|
||||
Enforce UDP encapsulation by faking NAT-D payloads.
|
||||
|
||||
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.
|
||||
|
||||
Usually this is not required, but it can help to work around connectivity
|
||||
issues with too restrictive intermediary firewalls.
|
||||
|
||||
connections.<conn>.mobike = yes
|
||||
Enables MOBIKE on IKEv2 connections.
|
||||
|
||||
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.
|
||||
|
||||
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.<conn>.dpd_delay = 0s
|
||||
Interval of liveness checks (DPD).
|
||||
|
||||
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.<conn>.dpd_timeout = 0s
|
||||
Timeout for DPD checks (IKEV1 only).
|
||||
|
||||
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.<conn>.fragmentation = no
|
||||
Use IKEv1 UDP packet fragmentation (_yes_, _no_ or _force_).
|
||||
|
||||
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.
|
||||
|
||||
IKE fragmentation is currently not supported with IKEv2.
|
||||
|
||||
connections.<conn>.send_certreq = yes
|
||||
Send certificate requests payloads (_yes_ or _no_).
|
||||
|
||||
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.
|
||||
|
||||
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.<conn>.send_cert = ifasked
|
||||
Send certificate payloads (_yes_, _no_ or _ifasked_).
|
||||
|
||||
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.<conn>.keyingtries = 1
|
||||
Number of retransmission sequences to perform during initial connect.
|
||||
|
||||
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.<conn>.unique = no
|
||||
Connection uniqueness policy (_never_, _no_, _keep_ or _replace_).
|
||||
|
||||
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.
|
||||
|
||||
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.<conn>.reauth_time = 0s
|
||||
Time to schedule IKE reauthentication.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
connections.<conn>.rekey_time = 4h
|
||||
Time to schedule IKE rekeying.
|
||||
|
||||
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.
|
||||
|
||||
With the default value IKE rekeying is scheduled every 4 hours, minus the
|
||||
configured **rand_time**.
|
||||
|
||||
connections.<conn>.over_time = 10% of rekey_time/reauth_time
|
||||
Hard IKE_SA lifetime if rekey/reauth does not complete, as time.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
The default is 10% of the longer of **rekey_time** and **reauth_time**.
|
||||
|
||||
connections.<conn>.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.<conn>.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.<conn>.local<suffix> {}
|
||||
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.<conn>.local<suffix>.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.<conn>.local<suffix>.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.<conn>.local<suffix>.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.<conn>.local<suffix>.eap_id = id
|
||||
Client EAP-Identity to use in EAP-Identity exchange and the EAP method.
|
||||
|
||||
connections.<conn>.local<suffix>.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.<conn>.local<suffix>.xauth_id = id
|
||||
Client XAuth username used in the XAuth exchange.
|
||||
|
||||
connections.<conn>.remote<suffix> {}
|
||||
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.<conn>.remote<suffix>.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.<conn>.remote<suffix>.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.<conn>.remote<suffix>.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.<conn>.remote<suffix>.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.<conn>.remote<suffix>.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.<conn>.remote<suffix>.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.<conn>.children.<child> {}
|
||||
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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.updown =
|
||||
Updown script to invoke on CHILD_SA up and down events.
|
||||
|
||||
connections.<conn>.children.<child>.hostaccess = yes
|
||||
Hostaccess variable to pass to **updown** script.
|
||||
|
||||
connections.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.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<suffix> { # }
|
||||
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<suffix> { # }
|
||||
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<suffix>.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<suffix>.id<suffix> =
|
||||
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<suffix> { # }
|
||||
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<suffix>.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<suffix>.id<suffix> =
|
||||
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.<name> { # }
|
||||
Section defining a single pool with a unique name.
|
||||
|
||||
pools.<name>.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.<name>.<attr> =
|
||||
Comma separated list of additional attributes from type <attr>.
|
||||
|
||||
Comma separated list of additional attributes of type **<attr>**. 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, **<attr>** can
|
||||
be a numerical identifier, for which string attribute values are accepted
|
||||
as well.
|
||||
Reference in New Issue
Block a user