diff options
author | Martin Willi <martin@revosec.ch> | 2014-09-24 11:17:29 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2014-09-24 11:17:29 +0200 |
commit | 6fe02dda752cbb2c2389a3367a3b6e04add43425 (patch) | |
tree | 4c34d40922d0848bfa682b4819d983e4ff57c982 /src | |
parent | 2dee0a85a6923da94d669dc5de337dc5ef1806e7 (diff) | |
parent | 575d3ab19a73736bfa25833e1102d3473e5bc25a (diff) | |
download | strongswan-6fe02dda752cbb2c2389a3367a3b6e04add43425.tar.bz2 strongswan-6fe02dda752cbb2c2389a3367a3b6e04add43425.tar.xz |
Merge branch 'systemd'
Introduces a systemd specific charon-systemd IKE daemon based on libcharon.
Uses systemd APIs for startup control and journal logging and a new systemd
service unit using swanctl as configuration backend.
Diffstat (limited to 'src')
26 files changed, 906 insertions, 112 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 95c68d0c8..603c9d164 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,6 +60,10 @@ if USE_CHARON SUBDIRS += charon endif +if USE_SYSTEMD + SUBDIRS += charon-systemd +endif + if USE_NM SUBDIRS += charon-nm endif diff --git a/src/charon-nm/nm/nm_backend.c b/src/charon-nm/nm/nm_backend.c index ebebde2c0..613c2f6b5 100644 --- a/src/charon-nm/nm/nm_backend.c +++ b/src/charon-nm/nm/nm_backend.c @@ -174,5 +174,5 @@ void nm_backend_register() PLUGIN_SDEPEND(CERT_DECODE, CERT_X509), }; lib->plugins->add_static_features(lib->plugins, "nm-backend", features, - countof(features), TRUE); + countof(features), TRUE, NULL, NULL); } diff --git a/src/charon-systemd/.gitignore b/src/charon-systemd/.gitignore new file mode 100644 index 000000000..da7b648a5 --- /dev/null +++ b/src/charon-systemd/.gitignore @@ -0,0 +1 @@ +charon-systemd diff --git a/src/charon-systemd/Makefile.am b/src/charon-systemd/Makefile.am new file mode 100644 index 000000000..1b9ac150f --- /dev/null +++ b/src/charon-systemd/Makefile.am @@ -0,0 +1,19 @@ +sbin_PROGRAMS = charon-systemd + +charon_systemd_SOURCES = \ +charon-systemd.c + +charon-systemd.o : $(top_builddir)/config.status + +charon_systemd_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon \ + $(systemd_daemon_CFLAGS) $(systemd_journal_CFLAGS) \ + -DPLUGINS=\""${charon_plugins}\"" + +charon_systemd_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libhydra/libhydra.la \ + $(top_builddir)/src/libcharon/libcharon.la \ + $(systemd_daemon_LIBS) $(systemd_journal_LIBS) -lm $(PTHREADLIB) $(DLLIB) diff --git a/src/charon-systemd/charon-systemd.c b/src/charon-systemd/charon-systemd.c new file mode 100644 index 000000000..07444e812 --- /dev/null +++ b/src/charon-systemd/charon-systemd.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2006-2012 Tobias Brunner + * Copyright (C) 2005-2014 Martin Willi + * Copyright (C) 2006 Daniel Roethlisberger + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * 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 <signal.h> +#include <stdio.h> +#include <pthread.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <errno.h> + +/* won't make sense from our logging hook */ +#define SD_JOURNAL_SUPPRESS_LOCATION +#include <systemd/sd-daemon.h> +#include <systemd/sd-journal.h> + +#include <hydra.h> +#include <daemon.h> + +#include <library.h> +#include <utils/backtrace.h> +#include <threading/thread.h> +#include <threading/rwlock.h> + +/** + * hook in library for debugging messages + */ +extern void (*dbg) (debug_t group, level_t level, char *fmt, ...); + +/** + * Logging hook for library logs, using stderr output + */ +static void dbg_stderr(debug_t group, level_t level, char *fmt, ...) +{ + va_list args; + + if (level <= 1) + { + va_start(args, fmt); + fprintf(stderr, "00[%N] ", debug_names, group); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + } +} + +typedef struct journal_logger_t journal_logger_t; + +/** + * Logger implementation using systemd-journal + */ +struct journal_logger_t { + + /** + * Implements logger_t + */ + logger_t logger; + + /** + * Configured loglevels + */ + level_t levels[DBG_MAX]; + + /** + * Lock for levels + */ + rwlock_t *lock; +}; + +METHOD(logger_t, vlog, void, + journal_logger_t *this, debug_t group, level_t level, int thread, + ike_sa_t *ike_sa, const char *fmt, va_list args) +{ + char buf[4096], *msg = buf; + ssize_t len; + va_list copy; + + va_copy(copy, args); + len = vsnprintf(msg, sizeof(buf), fmt, copy); + va_end(copy); + + if (len >= sizeof(buf)) + { + len++; + msg = malloc(len); + va_copy(copy, args); + len = vsnprintf(msg, len, fmt, copy); + va_end(copy); + } + if (len > 0) + { + char unique[64] = "", name[256] = ""; + int priority; + + if (ike_sa) + { + snprintf(unique, sizeof(unique), "IKE_SA_UNIQUE_ID=%u", + ike_sa->get_unique_id(ike_sa)); + if (ike_sa->get_peer_cfg(ike_sa)) + { + snprintf(name, sizeof(name), "IKE_SA_NAME=%s", + ike_sa->get_name(ike_sa)); + } + } + switch (level) + { + case LEVEL_AUDIT: + priority = LOG_NOTICE; + break; + case LEVEL_CTRL: + priority = LOG_INFO; + break; + default: + priority = LOG_DEBUG; + break; + } + sd_journal_send( + "MESSAGE=%s", msg, + "MESSAGE_ID=57d2708c-d607-43bd-8c39-66bf%.8x", + chunk_hash_static(chunk_from_str((char*)fmt)), + "PRIORITY=%d", priority, + "GROUP=%N", debug_names, group, + "LEVEL=%d", level, + "THREAD=%d", thread, + unique[0] ? unique : NULL, + name[0] ? name : NULL, + NULL); + } + if (msg != buf) + { + free(msg); + } +} + +METHOD(logger_t, get_level, level_t, + journal_logger_t *this, debug_t group) +{ + level_t level; + + this->lock->read_lock(this->lock); + level = this->levels[group]; + this->lock->unlock(this->lock); + + return level; +} + +/** + * Reload journal logger configuration + */ +CALLBACK(journal_reload, bool, + journal_logger_t **journal) +{ + journal_logger_t *this = *journal; + debug_t group; + level_t def; + + def = lib->settings->get_int(lib->settings, "%s.journal.default", 1, lib->ns); + + this->lock->write_lock(this->lock); + for (group = 0; group < DBG_MAX; group++) + { + this->levels[group] = + lib->settings->get_int(lib->settings, + "%s.journal.%N", def, lib->ns, debug_lower_names, group); + } + this->lock->unlock(this->lock); + + charon->bus->add_logger(charon->bus, &this->logger); + + return TRUE; +} + +/** + * Initialize/deinitialize journal logger + */ +static bool journal_register(void *plugin, plugin_feature_t *feature, + bool reg, journal_logger_t **logger) +{ + journal_logger_t *this; + + if (reg) + { + INIT(this, + .logger = { + .vlog = _vlog, + .get_level = _get_level, + }, + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + journal_reload(&this); + + *logger = this; + return TRUE; + } + else + { + this = *logger; + + charon->bus->remove_logger(charon->bus, &this->logger); + + this->lock->destroy(this->lock); + free(this); + + return TRUE; + } +} + +/** + * Run the daemon and handle unix signals + */ +static int run() +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_BLOCK, &set, NULL); + + sd_notify(0, "READY=1\n"); + + while (TRUE) + { + int sig, error; + + error = sigwait(&set, &sig); + if (error) + { + DBG1(DBG_DMN, "waiting for signal failed: %s", strerror(error)); + return SS_RC_INITIALIZATION_FAILED; + } + switch (sig) + { + case SIGTERM: + { + DBG1(DBG_DMN, "SIGTERM received, shutting down"); + charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig); + return 0; + } + default: + { + DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig); + break; + } + } + } +} + +/** + * lookup UID and GID + */ +static bool lookup_uid_gid() +{ +#ifdef IPSEC_USER + if (!lib->caps->resolve_uid(lib->caps, IPSEC_USER)) + { + return FALSE; + } +#endif /* IPSEC_USER */ +#ifdef IPSEC_GROUP + if (!lib->caps->resolve_gid(lib->caps, IPSEC_GROUP)) + { + return FALSE; + } +#endif /* IPSEC_GROUP */ + return TRUE; +} + +/** + * Handle SIGSEGV/SIGILL signals raised by threads + */ +static void segv_handler(int signal) +{ + backtrace_t *backtrace; + + DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal); + backtrace = backtrace_create(2); + backtrace->log(backtrace, NULL, TRUE); + backtrace->log(backtrace, stderr, TRUE); + backtrace->destroy(backtrace); + + DBG1(DBG_DMN, "killing ourself, received critical signal"); + abort(); +} + +/** + * The journal logger instance + */ +static journal_logger_t *journal; + +/** + * Journal static features + */ +static plugin_feature_t features[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)journal_register, &journal), + PLUGIN_PROVIDE(CUSTOM, "systemd-journal"), +}; + +/** + * Main function, starts the daemon. + */ +int main(int argc, char *argv[]) +{ + struct sigaction action; + struct utsname utsname; + + dbg = dbg_stderr; + + if (uname(&utsname) != 0) + { + memset(&utsname, 0, sizeof(utsname)); + } + + sd_notifyf(0, "STATUS=Starting charon-systemd, strongSwan %s, %s %s, %s", + VERSION, utsname.sysname, utsname.release, utsname.machine); + + atexit(library_deinit); + if (!library_init(NULL, "charon-systemd")) + { + sd_notifyf(0, "STATUS=libstrongswan initialization failed"); + return SS_RC_INITIALIZATION_FAILED; + } + if (lib->integrity && + !lib->integrity->check_file(lib->integrity, "charon-systemd", argv[0])) + { + sd_notifyf(0, "STATUS=integrity check of charon-systemd failed"); + return SS_RC_INITIALIZATION_FAILED; + } + atexit(libhydra_deinit); + if (!libhydra_init()) + { + sd_notifyf(0, "STATUS=libhydra initialization failed"); + return SS_RC_INITIALIZATION_FAILED; + } + atexit(libcharon_deinit); + if (!libcharon_init()) + { + sd_notifyf(0, "STATUS=libcharon initialization failed"); + return SS_RC_INITIALIZATION_FAILED; + } + if (!lookup_uid_gid()) + { + sd_notifyf(0, "STATUS=unkown uid/gid"); + return SS_RC_INITIALIZATION_FAILED; + } + charon->load_loggers(charon, NULL, FALSE); + + lib->plugins->add_static_features(lib->plugins, lib->ns, features, + countof(features), TRUE, journal_reload, &journal); + + if (!charon->initialize(charon, PLUGINS)) + { + sd_notifyf(0, "STATUS=charon initialization failed"); + return SS_RC_INITIALIZATION_FAILED; + } + lib->plugins->status(lib->plugins, LEVEL_CTRL); + + if (!lib->caps->drop(lib->caps)) + { + sd_notifyf(0, "STATUS=dropping capabilities failed"); + return SS_RC_INITIALIZATION_FAILED; + } + + /* add handler for SEGV and ILL, + * INT, TERM and HUP are handled by sigwait() in run() */ + action.sa_handler = segv_handler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + sigaddset(&action.sa_mask, SIGTERM); + sigaddset(&action.sa_mask, SIGHUP); + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGILL, &action, NULL); + sigaction(SIGBUS, &action, NULL); + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + + pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL); + + charon->start(charon); + + sd_notifyf(0, "STATUS=charon-systemd running, strongSwan %s, %s %s, %s", + VERSION, utsname.sysname, utsname.release, utsname.machine); + + return run(); +} diff --git a/src/charon-tkm/src/charon-tkm.c b/src/charon-tkm/src/charon-tkm.c index 9a22f9ad9..a6770fc50 100644 --- a/src/charon-tkm/src/charon-tkm.c +++ b/src/charon-tkm/src/charon-tkm.c @@ -296,7 +296,7 @@ int main(int argc, char *argv[]) PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), }; lib->plugins->add_static_features(lib->plugins, "tkm-backend", features, - countof(features), TRUE); + countof(features), TRUE, NULL, NULL); if (!register_dh_mapping()) { diff --git a/src/charon-tkm/src/tkm/tkm_diffie_hellman.c b/src/charon-tkm/src/tkm/tkm_diffie_hellman.c index a34d0b1d4..67db5e6d8 100644 --- a/src/charon-tkm/src/tkm/tkm_diffie_hellman.c +++ b/src/charon-tkm/src/tkm/tkm_diffie_hellman.c @@ -159,7 +159,8 @@ int register_dh_mapping() } enumerator->destroy(enumerator); - lib->plugins->add_static_features(lib->plugins, "tkm-dh", f, countof(f), TRUE); + lib->plugins->add_static_features(lib->plugins, "tkm-dh", f, countof(f), + TRUE, NULL, NULL); if (count > 0) { diff --git a/src/charon-tkm/tests/tests.c b/src/charon-tkm/tests/tests.c index 18754c717..80894a133 100644 --- a/src/charon-tkm/tests/tests.c +++ b/src/charon-tkm/tests/tests.c @@ -64,7 +64,7 @@ static bool test_runner_init(bool init) PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"), }; lib->plugins->add_static_features(lib->plugins, "tkm-tests", features, - countof(features), TRUE); + countof(features), TRUE, NULL, NULL); lib->settings->set_int(lib->settings, "%s.dh_mapping.%d", 1, lib->ns, MODP_3072_BIT); diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c index 32bf28f09..e6c9126d7 100644 --- a/src/frontends/android/jni/libandroidbridge/charonservice.c +++ b/src/frontends/android/jni/libandroidbridge/charonservice.c @@ -543,7 +543,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder, charonservice = &this->public; lib->plugins->add_static_features(lib->plugins, "androidbridge", features, - countof(features), TRUE); + countof(features), TRUE, NULL, NULL); #ifdef USE_BYOD if (byod) @@ -556,7 +556,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder, }; lib->plugins->add_static_features(lib->plugins, "android-byod", - byod_features, countof(byod_features), TRUE); + byod_features, countof(byod_features), TRUE, NULL, NULL); } #endif } diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index a89995a51..3ae7c4e6f 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -593,7 +593,7 @@ METHOD(daemon_t, initialize, bool, PLUGIN_DEPENDS(CUSTOM, "socket"), }; lib->plugins->add_static_features(lib->plugins, lib->ns, features, - countof(features), TRUE); + countof(features), TRUE, NULL, NULL); /* load plugins, further infrastructure may need it */ if (!lib->plugins->load(lib->plugins, plugins)) diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 3cd008162..292a40032 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -450,6 +450,17 @@ CALLBACK(uninstall, vici_message_t*, return send_reply(this, "policy '%s' not found", child); } +CALLBACK(reload_settings, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + if (lib->settings->load_files(lib->settings, lib->conf, FALSE)) + { + lib->plugins->reload(lib->plugins, NULL); + return send_reply(this, NULL); + } + return send_reply(this, "reloading '%s' failed", lib->conf); +} + static void manage_command(private_vici_control_t *this, char *name, vici_command_cb_t cb, bool reg) { @@ -466,6 +477,7 @@ static void manage_commands(private_vici_control_t *this, bool reg) manage_command(this, "terminate", terminate, reg); manage_command(this, "install", install, reg); manage_command(this, "uninstall", uninstall, reg); + manage_command(this, "reload-settings", reload_settings, reg); this->dispatcher->manage_event(this->dispatcher, "control-log", reg); } diff --git a/src/libstrongswan/plugins/plugin_loader.c b/src/libstrongswan/plugins/plugin_loader.c index c23f2f03f..1fec1b3ea 100644 --- a/src/libstrongswan/plugins/plugin_loader.c +++ b/src/libstrongswan/plugins/plugin_loader.c @@ -218,6 +218,16 @@ typedef struct { char *name; /** + * Optional reload function for features + */ + bool (*reload)(void *data); + + /** + * User data to pass to reload function + */ + void *reload_data; + + /** * Static plugin features */ plugin_feature_t *features; @@ -242,6 +252,16 @@ METHOD(plugin_t, get_static_features, int, return this->count; } +METHOD(plugin_t, static_reload, bool, + static_features_t *this) +{ + if (this->reload) + { + return this->reload(this->reload_data); + } + return FALSE; +} + METHOD(plugin_t, static_destroy, void, static_features_t *this) { @@ -254,7 +274,8 @@ METHOD(plugin_t, static_destroy, void, * Create a wrapper around static plugin features. */ static plugin_t *static_features_create(const char *name, - plugin_feature_t features[], int count) + plugin_feature_t features[], int count, + bool (*reload)(void*), void *reload_data) { static_features_t *this; @@ -262,9 +283,12 @@ static plugin_t *static_features_create(const char *name, .public = { .get_name = _get_static_name, .get_features = _get_static_features, + .reload = _static_reload, .destroy = _static_destroy, }, .name = strdup(name), + .reload = reload, + .reload_data = reload_data, .features = calloc(count, sizeof(plugin_feature_t)), .count = count, ); @@ -904,12 +928,13 @@ static void purge_plugins(private_plugin_loader_t *this) METHOD(plugin_loader_t, add_static_features, void, private_plugin_loader_t *this, const char *name, - plugin_feature_t features[], int count, bool critical) + plugin_feature_t features[], int count, bool critical, + bool (*reload)(void*), void *reload_data) { plugin_entry_t *entry; plugin_t *plugin; - plugin = static_features_create(name, features, count); + plugin = static_features_create(name, features, count, reload, reload_data); INIT(entry, .plugin = plugin, diff --git a/src/libstrongswan/plugins/plugin_loader.h b/src/libstrongswan/plugins/plugin_loader.h index fec57ce98..6be6a909c 100644 --- a/src/libstrongswan/plugins/plugin_loader.h +++ b/src/libstrongswan/plugins/plugin_loader.h @@ -44,6 +44,9 @@ struct plugin_loader_t { * If critical is TRUE load() will fail if any of the added features could * not be loaded. * + * If a reload callback function is given, it gets invoked for the + * registered feature set when reload() is invoked on the plugin_loader. + * * @note The name should be unique otherwise a plugin with the same name is * not loaded. * @@ -51,10 +54,13 @@ struct plugin_loader_t { * @param features array of plugin features * @param count number of features in the array * @param critical TRUE if the features are critical + * @param reload feature reload callback, or NULL + * @param reload_data user data to pass to reload callback */ void (*add_static_features) (plugin_loader_t *this, const char *name, struct plugin_feature_t *features, int count, - bool critical); + bool critical, bool (*reload)(void*), + void *reload_data); /** * Load a list of plugins. diff --git a/src/pt-tls-client/pt-tls-client.c b/src/pt-tls-client/pt-tls-client.c index 8b41ae25e..a8d45b54f 100644 --- a/src/pt-tls-client/pt-tls-client.c +++ b/src/pt-tls-client/pt-tls-client.c @@ -227,7 +227,7 @@ static void init() options = options_create(); lib->plugins->add_static_features(lib->plugins, "pt-tls-client", features, - countof(features), TRUE); + countof(features), TRUE, NULL, NULL); if (!lib->plugins->load(lib->plugins, lib->settings->get_str(lib->settings, "pt-tls-client.load", PLUGINS))) { diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 385737ad4..dec7d62ed 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -10,12 +10,14 @@ swanctl_SOURCES = \ 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/load_all.c \ + commands/load_conns.c commands/load_conns.h \ + commands/load_creds.c commands/load_creds.h \ + commands/load_pools.c commands/load_pools.h \ commands/log.c \ commands/version.c \ commands/stats.c \ + commands/reload_settings.c \ swanctl.c swanctl.h swanctl_LDADD = \ diff --git a/src/swanctl/command.c b/src/swanctl/command.c index e488273bf..dbe16c3b7 100644 --- a/src/swanctl/command.c +++ b/src/swanctl/command.c @@ -220,7 +220,7 @@ int command_usage(char *error, ...) { for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { - fprintf(out, " swanctl --%-10s (-%c) %s\n", + fprintf(out, " swanctl --%-15s (-%c) %s\n", cmds[i].cmd, cmds[i].op, cmds[i].description); } } diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 8510fa44d..2d78a24da 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 16 +#define MAX_COMMANDS 18 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/load_all.c b/src/swanctl/commands/load_all.c new file mode 100644 index 000000000..f47fee5b4 --- /dev/null +++ b/src/swanctl/commands/load_all.c @@ -0,0 +1,103 @@ +/* + * 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 "load_creds.h" +#include "load_pools.h" +#include "load_conns.h" + +static int load_all(vici_conn_t *conn) +{ + bool clear = FALSE, noprompt = FALSE; + command_format_options_t format = COMMAND_FORMAT_NONE; + settings_t *cfg; + int ret = 0; + char *arg; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'c': + clear = TRUE; + continue; + case 'n': + noprompt = TRUE; + continue; + case 'P': + format |= COMMAND_FORMAT_PRETTY; + /* fall through to raw */ + case 'r': + format |= COMMAND_FORMAT_RAW; + continue; + case EOF: + break; + default: + return command_usage("invalid --load-all option"); + } + break; + } + + cfg = settings_create(SWANCTL_CONF); + if (!cfg) + { + fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF); + return EINVAL; + } + + if (ret == 0) + { + ret = load_creds_cfg(conn, format, cfg, clear, noprompt); + } + if (ret == 0) + { + ret = load_pools_cfg(conn, format, cfg); + } + if (ret == 0) + { + ret = load_conns_cfg(conn, format, cfg); + } + + cfg->destroy(cfg); + + return ret; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + load_all, 'q', "load-all", "load credentials, pools and connections", + {"[--raw|--pretty] [--clear] [--noprompt]"}, + { + {"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"}, + {"pretty", 'P', 0, "dump raw response message in pretty print"}, + } + }); +} diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c index 7383f7a1e..de30d8eb4 100644 --- a/src/swanctl/commands/load_conns.c +++ b/src/swanctl/commands/load_conns.c @@ -20,6 +20,7 @@ #include "command.h" #include "swanctl.h" +#include "load_conns.h" /** * Check if we should handle a key as a list of comma separated values @@ -319,41 +320,16 @@ static bool unload_conn(vici_conn_t *conn, char *name, return ret; } -static int load_conns(vici_conn_t *conn) +/** + * See header. + */ +int load_conns_cfg(vici_conn_t *conn, command_format_options_t format, + settings_t *cfg) { u_int found = 0, loaded = 0, unloaded = 0; - command_format_options_t format = COMMAND_FORMAT_NONE; - char *arg, *section; + char *section; enumerator_t *enumerator; linked_list_t *conns; - settings_t *cfg; - - while (TRUE) - { - switch (command_getopt(&arg)) - { - case 'h': - return command_usage(NULL); - case 'P': - format |= COMMAND_FORMAT_PRETTY; - /* fall through to raw */ - case 'r': - format |= COMMAND_FORMAT_RAW; - continue; - case 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, format); @@ -369,8 +345,6 @@ static int load_conns(vici_conn_t *conn) } enumerator->destroy(enumerator); - cfg->destroy(cfg); - /* unload all connection in daemon, but not in file */ while (conns->remove_first(conns, (void**)§ion) == SUCCESS) { @@ -402,6 +376,47 @@ static int load_conns(vici_conn_t *conn) return EINVAL; } +static int load_conns(vici_conn_t *conn) +{ + command_format_options_t format = COMMAND_FORMAT_NONE; + settings_t *cfg; + char *arg; + int ret; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'P': + format |= COMMAND_FORMAT_PRETTY; + /* fall through to raw */ + case 'r': + format |= COMMAND_FORMAT_RAW; + continue; + case 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; + } + + ret = load_conns_cfg(conn, format, cfg); + + cfg->destroy(cfg); + + return ret; +} + /** * Register the command. */ diff --git a/src/swanctl/commands/load_conns.h b/src/swanctl/commands/load_conns.h new file mode 100644 index 000000000..1e7abdea4 --- /dev/null +++ b/src/swanctl/commands/load_conns.h @@ -0,0 +1,26 @@ +/* + * 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" + +/** + * Load all connections from configuration file + * + * @param conn vici connection to load to + * @param format output format + * @param cfg configuration to load from + */ +int load_conns_cfg(vici_conn_t *conn, command_format_options_t format, + settings_t *cfg); diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c index f77084c60..86ee3c179 100644 --- a/src/swanctl/commands/load_creds.c +++ b/src/swanctl/commands/load_creds.c @@ -21,6 +21,7 @@ #include "command.h" #include "swanctl.h" +#include "load_creds.h" #include <credentials/sets/mem_cred.h> #include <credentials/sets/callback_cred.h> @@ -484,13 +485,50 @@ static bool clear_creds(vici_conn_t *conn, command_format_options_t format) return TRUE; } +/** + * See header. + */ +int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, + settings_t *cfg, bool clear, bool noprompt) +{ + enumerator_t *enumerator; + char *section; + + if (clear) + { + if (!clear_creds(conn, format)) + { + return ECONNREFUSED; + } + } + + load_certs(conn, format, "x509", SWANCTL_X509DIR); + load_certs(conn, format, "x509ca", SWANCTL_X509CADIR); + load_certs(conn, format, "x509aa", SWANCTL_X509AADIR); + load_certs(conn, format, "x509crl", SWANCTL_X509CRLDIR); + load_certs(conn, format, "x509ac", SWANCTL_X509ACDIR); + + load_keys(conn, format, noprompt, cfg, "rsa", SWANCTL_RSADIR); + load_keys(conn, format, noprompt, cfg, "ecdsa", SWANCTL_ECDSADIR); + load_keys(conn, format, noprompt, cfg, "any", SWANCTL_PKCS8DIR); + + enumerator = cfg->create_section_enumerator(cfg, "secrets"); + while (enumerator->enumerate(enumerator, §ion)) + { + load_secret(conn, cfg, section, format); + } + enumerator->destroy(enumerator); + + return 0; +} + static int load_creds(vici_conn_t *conn) { bool clear = FALSE, noprompt = FALSE; command_format_options_t format = COMMAND_FORMAT_NONE; - enumerator_t *enumerator; settings_t *cfg; - char *arg, *section; + char *arg; + int ret; while (TRUE) { @@ -518,14 +556,6 @@ static int load_creds(vici_conn_t *conn) break; } - if (clear) - { - if (!clear_creds(conn, format)) - { - return ECONNREFUSED; - } - } - cfg = settings_create(SWANCTL_CONF); if (!cfg) { @@ -533,26 +563,11 @@ static int load_creds(vici_conn_t *conn) return EINVAL; } - load_certs(conn, format, "x509", SWANCTL_X509DIR); - load_certs(conn, format, "x509ca", SWANCTL_X509CADIR); - load_certs(conn, format, "x509aa", SWANCTL_X509AADIR); - load_certs(conn, format, "x509crl", SWANCTL_X509CRLDIR); - load_certs(conn, format, "x509ac", SWANCTL_X509ACDIR); - - load_keys(conn, format, noprompt, cfg, "rsa", SWANCTL_RSADIR); - load_keys(conn, format, noprompt, cfg, "ecdsa", SWANCTL_ECDSADIR); - load_keys(conn, format, noprompt, cfg, "any", SWANCTL_PKCS8DIR); - - enumerator = cfg->create_section_enumerator(cfg, "secrets"); - while (enumerator->enumerate(enumerator, §ion)) - { - load_secret(conn, cfg, section, format); - } - enumerator->destroy(enumerator); + ret = load_creds_cfg(conn, format, cfg, clear, noprompt); cfg->destroy(cfg); - return 0; + return ret; } /** @@ -562,7 +577,7 @@ static void __attribute__ ((constructor))reg() { command_register((command_t) { load_creds, 's', "load-creds", "(re-)load credentials", - {"[--raw|--pretty]"}, + {"[--raw|--pretty] [--clear] [--noprompt]"}, { {"help", 'h', 0, "show usage information"}, {"clear", 'c', 0, "clear previously loaded credentials"}, diff --git a/src/swanctl/commands/load_creds.h b/src/swanctl/commands/load_creds.h new file mode 100644 index 000000000..7f689ad71 --- /dev/null +++ b/src/swanctl/commands/load_creds.h @@ -0,0 +1,28 @@ +/* + * 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" + +/** + * Load all credentials from configuration file + * + * @param conn vici connection to load to + * @param format output format + * @param cfg configuration to load from + * @param clear TRUE to clear existing credentials + * @param noprompt TRUE to skip any password prompt + */ +int load_creds_cfg(vici_conn_t *conn, command_format_options_t format, + settings_t *cfg, bool clear, bool noprompt); diff --git a/src/swanctl/commands/load_pools.c b/src/swanctl/commands/load_pools.c index 0ec56cc43..d7fbd1341 100644 --- a/src/swanctl/commands/load_pools.c +++ b/src/swanctl/commands/load_pools.c @@ -20,6 +20,7 @@ #include "command.h" #include "swanctl.h" +#include "load_pools.h" /** * Add a vici list from a comma separated string value @@ -192,41 +193,16 @@ static bool unload_pool(vici_conn_t *conn, char *name, return ret; } -static int load_pools(vici_conn_t *conn) +/** + * See header. + */ +int load_pools_cfg(vici_conn_t *conn, command_format_options_t format, + settings_t *cfg) { - command_format_options_t format = COMMAND_FORMAT_NONE; u_int found = 0, loaded = 0, unloaded = 0; - char *arg, *section; + char *section; enumerator_t *enumerator; linked_list_t *pools; - settings_t *cfg; - - while (TRUE) - { - switch (command_getopt(&arg)) - { - case 'h': - return command_usage(NULL); - case 'P': - format |= COMMAND_FORMAT_PRETTY; - /* fall through to raw */ - case 'r': - format |= COMMAND_FORMAT_RAW; - continue; - case 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, format); @@ -242,8 +218,6 @@ static int load_pools(vici_conn_t *conn) } enumerator->destroy(enumerator); - cfg->destroy(cfg); - /* unload all pools in daemon, but not in file */ while (pools->remove_first(pools, (void**)§ion) == SUCCESS) { @@ -275,6 +249,47 @@ static int load_pools(vici_conn_t *conn) return EINVAL; } +static int load_pools(vici_conn_t *conn) +{ + command_format_options_t format = COMMAND_FORMAT_NONE; + settings_t *cfg; + char *arg; + int ret; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'P': + format |= COMMAND_FORMAT_PRETTY; + /* fall through to raw */ + case 'r': + format |= COMMAND_FORMAT_RAW; + continue; + case 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; + } + + ret = load_pools_cfg(conn, format, cfg); + + cfg->destroy(cfg); + + return ret; +} + /** * Register the command. */ @@ -282,7 +297,7 @@ static void __attribute__ ((constructor))reg() { command_register((command_t) { load_pools, 'a', "load-pools", "(re-)load pool configuration", - {"[--raw|--pretty"}, + {"[--raw|--pretty]"}, { {"help", 'h', 0, "show usage information"}, {"raw", 'r', 0, "dump raw response message"}, diff --git a/src/swanctl/commands/load_pools.h b/src/swanctl/commands/load_pools.h new file mode 100644 index 000000000..f424db9f1 --- /dev/null +++ b/src/swanctl/commands/load_pools.h @@ -0,0 +1,26 @@ +/* + * 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" + +/** + * Load all pool definitions from configuration file + * + * @param conn vici connection to load to + * @param format output format + * @param cfg configuration to load from + */ +int load_pools_cfg(vici_conn_t *conn, command_format_options_t format, + settings_t *cfg); diff --git a/src/swanctl/commands/reload_settings.c b/src/swanctl/commands/reload_settings.c new file mode 100644 index 000000000..ecd633866 --- /dev/null +++ b/src/swanctl/commands/reload_settings.c @@ -0,0 +1,87 @@ +/* + * 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 reload_settings(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + char *arg; + int ret = 0; + command_format_options_t format = COMMAND_FORMAT_NONE; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'P': + format |= COMMAND_FORMAT_PRETTY; + /* fall through to raw */ + case 'r': + format |= COMMAND_FORMAT_RAW; + continue; + case EOF: + break; + default: + return command_usage("invalid --reload-settings option"); + } + break; + } + + req = vici_begin("reload-settings"); + res = vici_submit(req, conn); + if (!res) + { + fprintf(stderr, "reload-settings request failed: %s\n", strerror(errno)); + return errno; + } + if (format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "reload-settings reply", + format & COMMAND_FORMAT_PRETTY, stdout); + } + else + { + if (!streq(vici_find_str(res, "no", "success"), "yes")) + { + fprintf(stderr, "reload-settings 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) { + reload_settings, 'r', "reload-settings", "reload daemon strongswan.conf", + {"[--raw|--pretty]"}, + { + {"help", 'h', 0, "show usage information"}, + {"raw", 'r', 0, "dump raw response message"}, + {"pretty", 'P', 0, "dump raw response message in pretty print"}, + } + }); +} diff --git a/src/swanctl/swanctl.8.in b/src/swanctl/swanctl.8.in index d5f4fc631..543c10a67 100644 --- a/src/swanctl/swanctl.8.in +++ b/src/swanctl/swanctl.8.in @@ -62,6 +62,9 @@ list stored certificates .B "\-A, \-\-list\-pools" list loaded pool configurations .TP +.B "\-q, \-\-load\-all" +(re\-)load credentials, pools and connections +.TP .B "\-c, \-\-load\-conns" (re\-)load connection configuration .TP @@ -77,6 +80,9 @@ trace logging output .B "\-S, \-\-stats" show daemon infos and statistics .TP +.B "\-r, \-\-reload-settings" +reload strongswan.conf(5) configuration +.TP .B "\-v, \-\-version" show daemon version information .TP |