diff --git a/configure.ac b/configure.ac index 6dbcccf37..336057a92 100644 --- a/configure.ac +++ b/configure.ac @@ -265,6 +265,7 @@ ARG_ENABL_SET([medcli], [enable mediation client configuration database ARG_ENABL_SET([medsrv], [enable mediation server web frontend and daemon plugin.]) ARG_ENABL_SET([nm], [enable NetworkManager backend.]) ARG_DISBL_SET([scripts], [disable additional utilities (found in directory scripts).]) +ARG_ENABL_SET([swanctl], [enable swanctl configuration and control tool.]) ARG_ENABL_SET([tkm], [enable Trusted Key Manager support.]) ARG_DISBL_SET([tools], [disable additional utilities (scepclient and pki).]) ARG_ENABL_SET([aikgen], [enable AIK generator.]) @@ -399,6 +400,10 @@ if test x$fips_prf = xtrue; then fi fi +if test x$swanctl = xtrue; then + vici=true +fi + if test x$smp = xtrue -o x$tnccs_11 = xtrue -o x$tnc_ifmap = xtrue; then xml=true fi @@ -1389,6 +1394,7 @@ AM_CONDITIONAL(COVERAGE, test x$coverage = xtrue) AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue) AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue) AM_CONDITIONAL(USE_AIKGEN, test x$aikgen = xtrue) +AM_CONDITIONAL(USE_SWANCTL, test x$swanctl = xtrue) # ======================== # set global definitions @@ -1605,6 +1611,7 @@ AC_CONFIG_FILES([ src/checksum/Makefile src/conftest/Makefile src/pt-tls-client/Makefile + src/swanctl/Makefile scripts/Makefile testing/Makefile ]) diff --git a/src/Makefile.am b/src/Makefile.am index e76eb4398..38e4b834d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,10 @@ if USE_TOOLS SUBDIRS += scepclient pki endif +if USE_SWANCTL + SUBDIRS += swanctl +endif + if USE_CONFTEST SUBDIRS += conftest endif diff --git a/src/checksum/Makefile.am b/src/checksum/Makefile.am index 82bbadcf1..078c59790 100644 --- a/src/checksum/Makefile.am +++ b/src/checksum/Makefile.am @@ -104,6 +104,10 @@ if USE_TOOLS exes += $(DESTDIR)$(bindir)/pki endif +if USE_SWANCTL + exes += $(DESTDIR)$(sbindir)/swanctl +endif + if USE_ATTR_SQL exes += $(DESTDIR)$(ipsecdir)/pool endif diff --git a/src/swanctl/.gitignore b/src/swanctl/.gitignore new file mode 100644 index 000000000..1db645ba7 --- /dev/null +++ b/src/swanctl/.gitignore @@ -0,0 +1 @@ +swanctl diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am new file mode 100644 index 000000000..a63c8d9f7 --- /dev/null +++ b/src/swanctl/Makefile.am @@ -0,0 +1,16 @@ +sbin_PROGRAMS = swanctl + +swanctl_SOURCES = \ + command.c command.h \ + swanctl.c + +swanctl_LDADD = \ + $(top_builddir)/src/libcharon/plugins/vici/libvici.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la + +swanctl.o : $(top_builddir)/config.status + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon/plugins/vici \ + -DPLUGINS=\""${s_plugins}\"" diff --git a/src/swanctl/command.c b/src/swanctl/command.c new file mode 100644 index 000000000..29f6be97f --- /dev/null +++ b/src/swanctl/command.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "command.h" + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * Registered commands. + */ +static command_t cmds[MAX_COMMANDS]; + +/** + * active command. + */ +static int active = 0; + +/** + * number of registered commands + */ +static int registered = 0; + +/** + * help command index + */ +static int help_idx; + +/** + * Uri to connect to + */ +static char *uri = NULL; + +static int argc; + +static char **argv; + +static options_t *options; + +/** + * Global options used by all subcommands + */ +static struct option command_opts[MAX_COMMANDS > MAX_OPTIONS ? + MAX_COMMANDS : MAX_OPTIONS]; + +/** + * Global optstring used by all subcommands + */ +static char command_optstring[(MAX_COMMANDS > MAX_OPTIONS ? + MAX_COMMANDS : MAX_OPTIONS) * 3]; + +/** + * Build command_opts/command_optstr for the active command + */ +static void build_opts() +{ + int i, pos = 0; + + memset(command_opts, 0, sizeof(command_opts)); + memset(command_optstring, 0, sizeof(command_optstring)); + if (active == help_idx) + { + for (i = 0; cmds[i].cmd; i++) + { + command_opts[i].name = cmds[i].cmd; + command_opts[i].val = cmds[i].op; + command_optstring[i] = cmds[i].op; + } + } + else + { + for (i = 0; cmds[active].options[i].name; i++) + { + command_opts[i].name = cmds[active].options[i].name; + command_opts[i].has_arg = cmds[active].options[i].arg; + command_opts[i].val = cmds[active].options[i].op; + command_optstring[pos++] = cmds[active].options[i].op; + switch (cmds[active].options[i].arg) + { + case optional_argument: + command_optstring[pos++] = ':'; + /* FALL */ + case required_argument: + command_optstring[pos++] = ':'; + /* FALL */ + case no_argument: + default: + break; + } + } + } +} + +/** + * getopt_long wrapper + */ +int command_getopt(char **arg) +{ + int op; + + while (TRUE) + { + op = getopt_long(argc, argv, command_optstring, command_opts, NULL); + switch (op) + { + case '+': + if (!options->from(options, optarg, &argc, &argv, optind)) + { + /* a error value */ + return 255; + } + continue; + case 'v': + dbg_default_set_level(atoi(optarg)); + continue; + case 'u': + uri = optarg; + continue; + default: + *arg = optarg; + return op; + } + } +} + +/** + * Register a command + */ +void command_register(command_t command) +{ + int i; + + if (registered == MAX_COMMANDS) + { + fprintf(stderr, "unable to register command, please increase " + "MAX_COMMANDS\n"); + return; + } + + cmds[registered] = command; + /* append default options, but not to --help */ + if (!active) + { + for (i = 0; i < countof(cmds[registered].options) - 1; i++) + { + if (!cmds[registered].options[i].name) + { + break; + } + } + if (i > countof(cmds[registered].options) - 3) + { + fprintf(stderr, "command '%s' registered too many options, please " + "increase MAX_OPTIONS\n", command.cmd); + } + else + { + cmds[registered].options[i++] = (command_option_t) { + "debug", 'v', 1, "set debug level, default: 1" + }; + cmds[registered].options[i++] = (command_option_t) { + "options", '+', 1, "read command line options from file" + }; + cmds[registered].options[i++] = (command_option_t) { + "uri", 'u', 1, "service URI to connect to" + }; + } + } + registered++; +} + +/** + * Print usage text, with an optional error + */ +int command_usage(char *error, ...) +{ + va_list args; + FILE *out = stdout; + int i; + + if (error) + { + out = stderr; + fprintf(out, "Error: "); + va_start(args, error); + vfprintf(out, error, args); + va_end(args); + fprintf(out, "\n"); + } + fprintf(out, "strongSwan %s swanctl\n", VERSION); + + if (active == help_idx) + { + fprintf(out, "loaded plugins: %s\n", + lib->plugins->loaded_plugins(lib->plugins)); + } + + fprintf(out, "usage:\n"); + if (active == help_idx) + { + for (i = 0; cmds[i].cmd; i++) + { + fprintf(out, " swanctl --%-10s (-%c) %s\n", + cmds[i].cmd, cmds[i].op, cmds[i].description); + } + } + else + { + for (i = 0; cmds[active].line[i]; i++) + { + if (i == 0) + { + fprintf(out, " swanctl --%s %s\n", + cmds[active].cmd, cmds[active].line[i]); + } + else + { + fprintf(out, " %s\n", cmds[active].line[i]); + } + } + for (i = 0; cmds[active].options[i].name; i++) + { + fprintf(out, " --%-15s (-%c) %s\n", + cmds[active].options[i].name, cmds[active].options[i].op, + cmds[active].options[i].desc); + } + } + return error != NULL; +} + +/** + * Dispatch cleanup hook + */ +static void cleanup() +{ + options->destroy(options); +} + +/** + * Open vici connection, call a command + */ +static int call_command(command_t *cmd) +{ + vici_conn_t *conn; + int ret; + + conn = vici_connect(uri); + if (!conn) + { + command_usage("connecting to '%s' URI failed: %s", + uri ?: "default", strerror(errno)); + return errno; + } + ret = cmd->call(conn); + vici_disconnect(conn); + return ret; +} + +/** + * Dispatch commands. + */ +int command_dispatch(int c, char *v[]) +{ + int op, i; + + options = options_create(); + atexit(cleanup); + active = help_idx = registered; + argc = c; + argv = v; + command_register((command_t){NULL, 'h', "help", "show usage information"}); + + build_opts(); + op = getopt_long(c, v, command_optstring, command_opts, NULL); + for (i = 0; cmds[i].cmd; i++) + { + if (cmds[i].op == op) + { + active = i; + build_opts(); + if (help_idx == i) + { + return command_usage(NULL); + } + return call_command(&cmds[i]); + } + } + return command_usage(c > 1 ? "invalid operation" : NULL); +} diff --git a/src/swanctl/command.h b/src/swanctl/command.h new file mode 100644 index 000000000..699483bf1 --- /dev/null +++ b/src/swanctl/command.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup command command + * @{ @ingroup swanctl + */ + +#ifndef COMMAND_H_ +#define COMMAND_H_ + +#include +#include + +/** + * Maximum number of commands (+1). + */ +#define MAX_COMMANDS 11 + +/** + * Maximum number of options in a command (+3) + */ +#define MAX_OPTIONS 32 + +/** + * Maximum number of usage summary lines (+1) + */ +#define MAX_LINES 10 + +typedef struct command_t command_t; +typedef struct command_option_t command_option_t; +typedef enum command_type_t command_type_t; + +/** + * Option specification + */ +struct command_option_t { + /** long option string of the option */ + char *name; + /** short option character of the option */ + char op; + /** expected argument to option, no/req/opt_argument */ + int arg; + /** description of the option */ + char *desc; +}; + +/** + * Command specification. + */ +struct command_t { + /** Function implementing the command */ + int (*call)(vici_conn_t *conn); + /** short option character */ + char op; + /** long option string */ + char *cmd; + /** description of the command */ + char *description; + /** usage summary of the command */ + char *line[MAX_LINES]; + /** list of options the command accepts */ + command_option_t options[MAX_OPTIONS]; +}; + +/** + * Get the next option, as with getopt. + */ +int command_getopt(char **arg); + +/** + * Register a command. + */ +void command_register(command_t command); + +/** + * Dispatch commands. + */ +int command_dispatch(int argc, char *argv[]); + +/** + * Show usage information of active command. + */ +int command_usage(char *error, ...); + +#endif /** COMMAND_H_ @}*/ diff --git a/src/swanctl/swanctl.c b/src/swanctl/swanctl.c new file mode 100644 index 000000000..7aacf839d --- /dev/null +++ b/src/swanctl/swanctl.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "command.h" + +#include + +#include + +/** + * Cleanup library atexit() + */ +static void cleanup() +{ + lib->processor->cancel(lib->processor); + library_deinit(); +} + +/** + * Library initialization and operation parsing + */ +int main(int argc, char *argv[]) +{ + atexit(cleanup); + if (!library_init(NULL, "swanctl")) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (lib->integrity && + !lib->integrity->check_file(lib->integrity, "swanctl", argv[0])) + { + fprintf(stderr, "integrity check of swanctl failed\n"); + exit(SS_RC_DAEMON_INTEGRITY); + } + if (!lib->plugins->load(lib->plugins, + lib->settings->get_str(lib->settings, "swanctl.load", PLUGINS))) + { + exit(SS_RC_INITIALIZATION_FAILED); + } + dbg_default_set_level(0); + lib->processor->set_threads(lib->processor, 4); + dbg_default_set_level(0); + + return command_dispatch(argc, argv); +}