aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2013-05-06 16:01:25 +0200
committerMartin Willi <martin@revosec.ch>2013-05-06 16:01:25 +0200
commit922e2d1d62541454be7d84c7551a29a9948f853c (patch)
treea31602816effaafe08e52ab9a46f6cbbc58260a2
parentb4e9f74e428ab0f3026df4175d133b81b16c9d56 (diff)
parent68fc0fe32eb3d7d89c9639b44903b98a1e073a05 (diff)
downloadstrongswan-922e2d1d62541454be7d84c7551a29a9948f853c.tar.bz2
strongswan-922e2d1d62541454be7d84c7551a29a9948f853c.tar.xz
Merge branch 'charon-cmd'
Introduce a simple IKEv1/IKEv2 command line client, charon-cmd. It does not need any configuration files at all, but takes a few command line arguments to establish connections as a road warrior.
-rw-r--r--configure.in103
-rw-r--r--src/Makefile.am4
-rw-r--r--src/charon-cmd/.gitignore1
-rw-r--r--src/charon-cmd/Makefile.am25
-rw-r--r--src/charon-cmd/charon-cmd.c380
-rw-r--r--src/charon-cmd/cmd/cmd_connection.c445
-rw-r--r--src/charon-cmd/cmd/cmd_connection.h55
-rw-r--r--src/charon-cmd/cmd/cmd_creds.c169
-rw-r--r--src/charon-cmd/cmd/cmd_creds.h55
-rw-r--r--src/charon-cmd/cmd/cmd_options.c52
-rw-r--r--src/charon-cmd/cmd/cmd_options.h68
-rw-r--r--src/checksum/Makefile.am4
-rw-r--r--src/libcharon/control/controller.c9
-rw-r--r--src/libcharon/encoding/payloads/proposal_substructure.c6
-rw-r--r--src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c127
-rw-r--r--src/libstrongswan/utils/settings.c21
-rw-r--r--src/libstrongswan/utils/settings.h10
17 files changed, 1454 insertions, 80 deletions
diff --git a/configure.in b/configure.in
index 311b15c6b..06829755f 100644
--- a/configure.in
+++ b/configure.in
@@ -241,6 +241,7 @@ ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for m
ARG_ENABL_SET([unwind-backtraces],[use libunwind to create backtraces for memory leaks and segfaults.])
ARG_ENABL_SET([unit-tests], [enable unit tests using the check test framework.])
ARG_ENABL_SET([tkm], [enable Trusted Key Manager support.])
+ARG_ENABL_SET([cmd], [enable the command line IKE client charon-cmd.])
# ===================================
# option to disable default options
@@ -931,6 +932,7 @@ scripts_plugins=
manager_plugins=
medsrv_plugins=
nm_plugins=
+cmd_plugins=
# location specific lists for checksumming,
# for src/libcharon, src/libhydra and src/libstrongswan
@@ -939,63 +941,63 @@ h_plugins=
s_plugins=
ADD_PLUGIN([test-vectors], [s charon openac scepclient pki])
-ADD_PLUGIN([curl], [s charon scepclient scripts nm])
-ADD_PLUGIN([soup], [s charon scripts nm])
+ADD_PLUGIN([curl], [s charon scepclient scripts nm cmd])
+ADD_PLUGIN([soup], [s charon scripts nm cmd])
ADD_PLUGIN([unbound], [s charon scripts])
-ADD_PLUGIN([ldap], [s charon scepclient scripts nm])
+ADD_PLUGIN([ldap], [s charon scepclient scripts nm cmd])
ADD_PLUGIN([mysql], [s charon pool manager medsrv attest])
ADD_PLUGIN([sqlite], [s charon pool manager medsrv attest])
-ADD_PLUGIN([pkcs11], [s charon pki nm])
-ADD_PLUGIN([aes], [s charon openac scepclient pki scripts nm])
-ADD_PLUGIN([des], [s charon openac scepclient pki scripts nm])
-ADD_PLUGIN([blowfish], [s charon openac scepclient pki scripts nm])
-ADD_PLUGIN([sha1], [s charon openac scepclient pki scripts medsrv attest nm])
-ADD_PLUGIN([sha2], [s charon openac scepclient pki scripts medsrv attest nm])
-ADD_PLUGIN([md4], [s charon openac manager scepclient pki nm])
-ADD_PLUGIN([md5], [s charon openac scepclient pki scripts attest nm])
-ADD_PLUGIN([rdrand], [s charon openac scepclient pki scripts medsrv attest nm])
-ADD_PLUGIN([random], [s charon openac scepclient pki scripts medsrv attest nm])
-ADD_PLUGIN([nonce], [s charon nm])
-ADD_PLUGIN([x509], [s charon openac scepclient pki scripts attest nm])
-ADD_PLUGIN([revocation], [s charon nm])
-ADD_PLUGIN([constraints], [s charon nm])
+ADD_PLUGIN([pkcs11], [s charon pki nm cmd])
+ADD_PLUGIN([aes], [s charon openac scepclient pki scripts nm cmd])
+ADD_PLUGIN([des], [s charon openac scepclient pki scripts nm cmd])
+ADD_PLUGIN([blowfish], [s charon openac scepclient pki scripts nm cmd])
+ADD_PLUGIN([sha1], [s charon openac scepclient pki scripts medsrv attest nm cmd])
+ADD_PLUGIN([sha2], [s charon openac scepclient pki scripts medsrv attest nm cmd])
+ADD_PLUGIN([md4], [s charon openac manager scepclient pki nm cmd])
+ADD_PLUGIN([md5], [s charon openac scepclient pki scripts attest nm cmd])
+ADD_PLUGIN([rdrand], [s charon openac scepclient pki scripts medsrv attest nm cmd])
+ADD_PLUGIN([random], [s charon openac scepclient pki scripts medsrv attest nm cmd])
+ADD_PLUGIN([nonce], [s charon nm cmd])
+ADD_PLUGIN([x509], [s charon openac scepclient pki scripts attest nm cmd])
+ADD_PLUGIN([revocation], [s charon nm cmd])
+ADD_PLUGIN([constraints], [s charon nm cmd])
ADD_PLUGIN([pubkey], [s charon])
-ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm])
+ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([pkcs7], [s scepclient pki])
-ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm])
+ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([pgp], [s charon])
ADD_PLUGIN([dnskey], [s charon])
ADD_PLUGIN([ipseckey], [c charon])
-ADD_PLUGIN([pem], [s charon openac scepclient pki scripts manager medsrv attest nm])
+ADD_PLUGIN([pem], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([padlock], [s charon])
-ADD_PLUGIN([openssl], [s charon openac scepclient pki scripts manager medsrv attest nm])
-ADD_PLUGIN([gcrypt], [s charon openac scepclient pki scripts manager medsrv attest nm])
-ADD_PLUGIN([af-alg], [s charon openac scepclient pki scripts medsrv attest nm])
-ADD_PLUGIN([fips-prf], [s charon nm])
-ADD_PLUGIN([gmp], [s charon openac scepclient pki scripts manager medsrv attest nm])
-ADD_PLUGIN([agent], [s charon nm])
-ADD_PLUGIN([xcbc], [s charon nm])
-ADD_PLUGIN([cmac], [s charon nm])
-ADD_PLUGIN([hmac], [s charon scripts nm])
-ADD_PLUGIN([ctr], [s charon scripts nm])
-ADD_PLUGIN([ccm], [s charon scripts nm])
-ADD_PLUGIN([gcm], [s charon scripts nm])
+ADD_PLUGIN([openssl], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
+ADD_PLUGIN([gcrypt], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
+ADD_PLUGIN([af-alg], [s charon openac scepclient pki scripts medsrv attest nm cmd])
+ADD_PLUGIN([fips-prf], [s charon nm cmd])
+ADD_PLUGIN([gmp], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
+ADD_PLUGIN([agent], [s charon nm cmd])
+ADD_PLUGIN([xcbc], [s charon nm cmd])
+ADD_PLUGIN([cmac], [s charon nm cmd])
+ADD_PLUGIN([hmac], [s charon scripts nm cmd])
+ADD_PLUGIN([ctr], [s charon scripts nm cmd])
+ADD_PLUGIN([ccm], [s charon scripts nm cmd])
+ADD_PLUGIN([gcm], [s charon scripts nm cmd])
ADD_PLUGIN([attr], [h charon])
ADD_PLUGIN([attr-sql], [h charon])
ADD_PLUGIN([load-tester], [c charon])
-ADD_PLUGIN([kernel-pfkey], [h charon starter nm])
-ADD_PLUGIN([kernel-pfroute], [h charon starter nm])
+ADD_PLUGIN([kernel-pfkey], [h charon starter nm cmd])
+ADD_PLUGIN([kernel-pfroute], [h charon starter nm cmd])
ADD_PLUGIN([kernel-klips], [h charon starter])
-ADD_PLUGIN([kernel-netlink], [h charon starter nm])
-ADD_PLUGIN([resolve], [h charon])
-ADD_PLUGIN([socket-default], [c charon nm])
-ADD_PLUGIN([socket-dynamic], [c charon])
+ADD_PLUGIN([kernel-netlink], [h charon starter nm cmd])
+ADD_PLUGIN([resolve], [h charon cmd])
+ADD_PLUGIN([socket-default], [c charon nm cmd])
+ADD_PLUGIN([socket-dynamic], [c charon cmd])
ADD_PLUGIN([farp], [c charon])
ADD_PLUGIN([stroke], [c charon])
ADD_PLUGIN([smp], [c charon])
ADD_PLUGIN([sql], [c charon])
ADD_PLUGIN([updown], [c charon])
-ADD_PLUGIN([eap-identity], [c charon nm])
+ADD_PLUGIN([eap-identity], [c charon nm cmd])
ADD_PLUGIN([eap-sim], [c charon])
ADD_PLUGIN([eap-sim-file], [c charon])
ADD_PLUGIN([eap-sim-pcsc], [c charon])
@@ -1004,16 +1006,16 @@ ADD_PLUGIN([eap-aka-3gpp2], [c charon])
ADD_PLUGIN([eap-simaka-sql], [c charon])
ADD_PLUGIN([eap-simaka-pseudonym], [c charon])
ADD_PLUGIN([eap-simaka-reauth], [c charon])
-ADD_PLUGIN([eap-md5], [c charon nm])
-ADD_PLUGIN([eap-gtc], [c charon nm])
-ADD_PLUGIN([eap-mschapv2], [c charon nm])
+ADD_PLUGIN([eap-md5], [c charon nm cmd])
+ADD_PLUGIN([eap-gtc], [c charon nm cmd])
+ADD_PLUGIN([eap-mschapv2], [c charon nm cmd])
ADD_PLUGIN([eap-dynamic], [c charon])
ADD_PLUGIN([eap-radius], [c charon])
-ADD_PLUGIN([eap-tls], [c charon nm])
-ADD_PLUGIN([eap-ttls], [c charon nm])
-ADD_PLUGIN([eap-peap], [c charon nm])
+ADD_PLUGIN([eap-tls], [c charon nm cmd])
+ADD_PLUGIN([eap-ttls], [c charon nm cmd])
+ADD_PLUGIN([eap-peap], [c charon nm cmd])
ADD_PLUGIN([eap-tnc], [c charon])
-ADD_PLUGIN([xauth-generic], [c charon])
+ADD_PLUGIN([xauth-generic], [c charon cmd])
ADD_PLUGIN([xauth-eap], [c charon])
ADD_PLUGIN([xauth-pam], [c charon])
ADD_PLUGIN([xauth-noauth], [c charon])
@@ -1057,6 +1059,7 @@ AC_SUBST(scripts_plugins)
AC_SUBST(manager_plugins)
AC_SUBST(medsrv_plugins)
AC_SUBST(nm_plugins)
+AC_SUBST(cmd_plugins)
AC_SUBST(c_plugins)
AC_SUBST(h_plugins)
@@ -1209,9 +1212,9 @@ AM_CONDITIONAL(USE_NM, test x$nm = xtrue)
AM_CONDITIONAL(USE_TOOLS, test x$tools = xtrue)
AM_CONDITIONAL(USE_SCRIPTS, test x$scripts = xtrue)
AM_CONDITIONAL(USE_CONFTEST, test x$conftest = xtrue)
-AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$tools = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue)
-AM_CONDITIONAL(USE_LIBHYDRA, test x$charon = xtrue -o x$nm = xtrue -o x$tkm = xtrue)
-AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$nm = xtrue -o x$tkm = xtrue)
+AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$tools = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue)
+AM_CONDITIONAL(USE_LIBHYDRA, test x$charon = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue)
+AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue)
AM_CONDITIONAL(USE_LIBIPSEC, test x$libipsec = xtrue)
AM_CONDITIONAL(USE_LIBTNCIF, test x$tnc_tnccs = xtrue -o x$imcv = xtrue)
AM_CONDITIONAL(USE_LIBTNCCS, test x$tnc_tnccs = xtrue)
@@ -1230,6 +1233,7 @@ AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue)
AM_CONDITIONAL(USE_SILENT_RULES, test x$enable_silent_rules = xyes)
AM_CONDITIONAL(UNITTESTS, test x$unit_tests = xtrue)
AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue)
+AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue)
# ========================
# set global definitions
@@ -1332,6 +1336,7 @@ AC_CONFIG_FILES([
src/charon/Makefile
src/charon-nm/Makefile
src/charon-tkm/Makefile
+ src/charon-cmd/Makefile
src/libcharon/Makefile
src/libcharon/plugins/eap_aka/Makefile
src/libcharon/plugins/eap_aka_3gpp2/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 07953b0b0..47299b03c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,6 +104,10 @@ if USE_TKM
SUBDIRS += charon-tkm
endif
+if USE_CMD
+ SUBDIRS += charon-cmd
+endif
+
EXTRA_DIST = strongswan.conf
install-exec-local :
diff --git a/src/charon-cmd/.gitignore b/src/charon-cmd/.gitignore
new file mode 100644
index 000000000..c02dfbacc
--- /dev/null
+++ b/src/charon-cmd/.gitignore
@@ -0,0 +1 @@
+charon-cmd
diff --git a/src/charon-cmd/Makefile.am b/src/charon-cmd/Makefile.am
new file mode 100644
index 000000000..2c9f1ba1f
--- /dev/null
+++ b/src/charon-cmd/Makefile.am
@@ -0,0 +1,25 @@
+sbin_PROGRAMS = charon-cmd
+
+charon_cmd_SOURCES = \
+ cmd/cmd_options.h cmd/cmd_options.c \
+ cmd/cmd_connection.h cmd/cmd_connection.c \
+ cmd/cmd_creds.h cmd/cmd_creds.c \
+ charon-cmd.c
+
+charon-cmd.o : $(top_builddir)/config.status
+
+INCLUDES = \
+ -I$(top_srcdir)/src/libstrongswan \
+ -I$(top_srcdir)/src/libhydra \
+ -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = \
+ -DIPSEC_DIR=\"${ipsecdir}\" \
+ -DIPSEC_PIDDIR=\"${piddir}\" \
+ -DPLUGINS=\""${cmd_plugins}\""
+
+charon_cmd_LDADD = \
+ $(top_builddir)/src/libstrongswan/libstrongswan.la \
+ $(top_builddir)/src/libhydra/libhydra.la \
+ $(top_builddir)/src/libcharon/libcharon.la \
+ -lm $(PTHREADLIB) $(DLLIB)
diff --git a/src/charon-cmd/charon-cmd.c b/src/charon-cmd/charon-cmd.c
new file mode 100644
index 000000000..5f27255a9
--- /dev/null
+++ b/src/charon-cmd/charon-cmd.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2006-2012 Tobias Brunner
+ * Copyright (C) 2005-2013 Martin Willi
+ * Copyright (C) 2006 Daniel Roethlisberger
+ * Copyright (C) 2005 Jan Hutter
+ * 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 <stdio.h>
+#define _POSIX_PTHREAD_SEMANTICS /* for two param sigwait on OpenSolaris */
+#include <signal.h>
+#undef _POSIX_PTHREAD_SEMANTICS
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <library.h>
+#include <hydra.h>
+#include <daemon.h>
+#include <utils/backtrace.h>
+#include <threading/thread.h>
+
+#include "cmd/cmd_options.h"
+#include "cmd/cmd_connection.h"
+#include "cmd/cmd_creds.h"
+
+/**
+ * Loglevel configuration
+ */
+static level_t levels[DBG_MAX];
+
+/**
+ * Connection to initiate
+ */
+static cmd_connection_t *conn;
+
+/**
+ * Credential backend
+ */
+static cmd_creds_t *creds;
+
+/**
+ * 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);
+ }
+}
+
+/**
+ * Clean up connection definition atexit()
+ */
+static void cleanup_conn()
+{
+ DESTROY_IF(conn);
+}
+
+/**
+ * Clean up credentials atexit()
+ */
+static void cleanup_creds()
+{
+ DESTROY_IF(creds);
+}
+
+/**
+ * Run the daemon and handle unix signals
+ */
+static int run()
+{
+ sigset_t set;
+
+ /* handle SIGINT, SIGHUP and SIGTERM in this handler */
+ sigemptyset(&set);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGUSR1);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ while (TRUE)
+ {
+ int sig;
+ int error;
+
+ error = sigwait(&set, &sig);
+ if (error)
+ {
+ DBG1(DBG_DMN, "error %d while waiting for a signal", error);
+ return 1;
+ }
+ switch (sig)
+ {
+ case SIGHUP:
+ {
+ DBG1(DBG_DMN, "signal of type SIGHUP received. Reloading "
+ "configuration");
+ if (lib->settings->load_files(lib->settings, NULL, FALSE))
+ {
+ charon->load_loggers(charon, levels, TRUE);
+ lib->plugins->reload(lib->plugins, NULL);
+ }
+ else
+ {
+ DBG1(DBG_DMN, "reloading config failed, keeping old");
+ }
+ break;
+ }
+ case SIGINT:
+ {
+ DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down");
+ charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
+ return 0;
+ }
+ case SIGTERM:
+ {
+ DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down");
+ charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
+ return 0;
+ }
+ case SIGUSR1:
+ { /* an error occured */
+ charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
+ return 1;
+ }
+ default:
+ {
+ DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig);
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * lookup UID and GID
+ */
+static bool lookup_uid_gid()
+{
+#ifdef IPSEC_USER
+ if (!charon->caps->resolve_uid(charon->caps, IPSEC_USER))
+ {
+ return FALSE;
+ }
+#endif
+#ifdef IPSEC_GROUP
+ if (!charon->caps->resolve_gid(charon->caps, IPSEC_GROUP))
+ {
+ return FALSE;
+ }
+#endif
+ 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, stderr, TRUE);
+ backtrace->destroy(backtrace);
+
+ DBG1(DBG_DMN, "killing ourself, received critical signal");
+ abort();
+}
+
+/**
+ * Print command line usage and exit
+ */
+static void usage(FILE *out, char *msg, char *binary)
+{
+ int i, line, pre, post, padto = 0, spacing = 2;
+
+ for (i = 0; i < CMD_OPT_COUNT; i++)
+ {
+ padto = max(padto, strlen(cmd_options[i].name) +
+ strlen(cmd_options[i].arg));
+ }
+ padto += spacing;
+
+ if (msg)
+ {
+ fprintf(out, "%s\n", msg);
+ }
+ fprintf(out, "Usage: %s\n", binary);
+ for (i = 0; i < CMD_OPT_COUNT; i++)
+ {
+ switch (cmd_options[i].has_arg)
+ {
+ case required_argument:
+ pre = '<';
+ post = '>';
+ break;
+ case optional_argument:
+ pre = '[';
+ post = ']';
+ break;
+ case no_argument:
+ default:
+ pre = post = ' ';
+ break;
+ }
+ fprintf(out, " --%s %c%s%c %-*s%s\n",
+ cmd_options[i].name,
+ pre, cmd_options[i].arg, post,
+ padto - strlen(cmd_options[i].name) - strlen(cmd_options[i].arg), "",
+ cmd_options[i].desc);
+ for (line = 0; line < countof(cmd_options[i].lines); line++)
+ {
+ if (cmd_options[i].lines[line])
+ {
+ fprintf(out, "%-*s %s\n",
+ padto, "", cmd_options[i].lines[line]);
+ }
+ }
+ }
+}
+
+/**
+ * Handle command line options
+ */
+static void handle_arguments(int argc, char *argv[])
+{
+ struct option long_opts[CMD_OPT_COUNT + 1] = {};
+ int i, opt;
+
+ for (i = 0; i < CMD_OPT_COUNT; i++)
+ {
+ long_opts[i].name = cmd_options[i].name;
+ long_opts[i].val = cmd_options[i].id;
+ long_opts[i].has_arg = cmd_options[i].has_arg;
+ }
+ while (TRUE)
+ {
+ bool handled = FALSE;
+
+ opt = getopt_long(argc, argv, "", long_opts, NULL);
+ switch (opt)
+ {
+ case EOF:
+ break;
+ case CMD_OPT_HELP:
+ usage(stdout, NULL, argv[0]);
+ exit(0);
+ case CMD_OPT_VERSION:
+ printf("%s, strongSwan %s\n", "charon-cmd", VERSION);
+ exit(0);
+ default:
+ handled |= conn->handle(conn, opt, optarg);
+ handled |= creds->handle(creds, opt, optarg);
+ if (handled)
+ {
+ continue;
+ }
+ usage(stderr, NULL, argv[0]);
+ exit(1);
+ }
+ break;
+ }
+}
+
+/**
+ * Main function, starts the daemon.
+ */
+int main(int argc, char *argv[])
+{
+ struct sigaction action;
+ struct utsname utsname;
+ int group;
+
+ dbg = dbg_stderr;
+ atexit(library_deinit);
+ if (!library_init(NULL))
+ {
+ exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+ }
+ if (lib->integrity)
+ {
+ if (!lib->integrity->check_file(lib->integrity, "charon-cmd", argv[0]))
+ {
+ exit(SS_RC_DAEMON_INTEGRITY);
+ }
+ }
+ atexit(libhydra_deinit);
+ if (!libhydra_init("charon-cmd"))
+ {
+ exit(SS_RC_INITIALIZATION_FAILED);
+ }
+ atexit(libcharon_deinit);
+ if (!libcharon_init("charon-cmd"))
+ {
+ exit(SS_RC_INITIALIZATION_FAILED);
+ }
+ for (group = 0; group < DBG_MAX; group++)
+ {
+ levels[group] = LEVEL_CTRL;
+ }
+ charon->load_loggers(charon, levels, TRUE);
+
+ if (!lookup_uid_gid())
+ {
+ exit(SS_RC_INITIALIZATION_FAILED);
+ }
+ lib->settings->set_default_str(lib->settings, "charon-cmd.port", "0");
+ lib->settings->set_default_str(lib->settings, "charon-cmd.port_nat_t", "0");
+ if (!charon->initialize(charon,
+ lib->settings->get_str(lib->settings, "charon-cmd.load", PLUGINS)))
+ {
+ exit(SS_RC_INITIALIZATION_FAILED);
+ }
+ if (!charon->caps->drop(charon->caps))
+ {
+ exit(SS_RC_INITIALIZATION_FAILED);
+ }
+
+ conn = cmd_connection_create();
+ atexit(cleanup_conn);
+ creds = cmd_creds_create();
+ atexit(cleanup_creds);
+
+ handle_arguments(argc, argv);
+
+ if (uname(&utsname) != 0)
+ {
+ memset(&utsname, 0, sizeof(utsname));
+ }
+ DBG1(DBG_DMN, "Starting charon-cmd IKE client (strongSwan %s, %s %s, %s)",
+ VERSION, utsname.sysname, utsname.release, utsname.machine);
+
+ /* 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);
+
+ /* start daemon with thread-pool */
+ charon->start(charon);
+ /* wait for signal */
+ return run();
+}
diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c
new file mode 100644
index 000000000..965b72bc0
--- /dev/null
+++ b/src/charon-cmd/cmd/cmd_connection.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 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 "cmd_connection.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+#include <utils/debug.h>
+#include <processing/jobs/callback_job.h>
+#include <threading/thread.h>
+#include <daemon.h>
+
+typedef enum profile_t profile_t;
+typedef struct private_cmd_connection_t private_cmd_connection_t;
+
+/**
+ * Connection profiles we support
+ */
+enum profile_t {
+ PROF_UNDEF,
+ PROF_V2_PUB,
+ PROF_V2_EAP,
+ PROF_V2_PUB_EAP,
+ PROF_V1_PUB,
+ PROF_V1_XAUTH,
+ PROF_V1_XAUTH_PSK,
+ PROF_V1_HYBRID,
+};
+
+ENUM(profile_names, PROF_V2_PUB, PROF_V1_HYBRID,
+ "ikev2-pub",
+ "ikev2-eap",
+ "ikev2-pub-eap",
+ "ikev1-pub",
+ "ikev1-xauth",
+ "ikev1-xauth-psk",
+ "ikev1-hybrid",
+);
+
+/**
+ * Private data of an cmd_connection_t object.
+ */
+struct private_cmd_connection_t {
+
+ /**
+ * Public cmd_connection_t interface.
+ */
+ cmd_connection_t public;
+
+ /**
+ * Process ID to terminate on failure
+ */
+ pid_t pid;
+
+ /**
+ * List of local traffic selectors
+ */
+ linked_list_t *local_ts;
+
+ /**
+ * List of remote traffic selectors
+ */
+ linked_list_t *remote_ts;
+
+ /**
+ * Hostname to connect to
+ */
+ char *host;
+
+ /**
+ * Server identity, or NULL to use host
+ */
+ char *server;
+
+ /**
+ * Local identity
+ */
+ char *identity;
+
+ /**
+ * Is a private key configured
+ */
+ bool key_seen;
+
+ /**
+ * Selected connection profile
+ */
+ profile_t profile;
+};
+
+/**
+ * Shut down application
+ */
+static void terminate(private_cmd_connection_t *this)
+{
+ kill(this->pid, SIGUSR1);
+}
+
+/**
+ * Create peer config with associated ike config
+ */
+static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
+{
+ ike_cfg_t *ike_cfg;
+ peer_cfg_t *peer_cfg;
+ u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
+ ike_version_t version = IKE_ANY;
+
+ switch (this->profile)
+ {
+ case PROF_UNDEF:
+ case PROF_V2_PUB:
+ case PROF_V2_EAP:
+ case PROF_V2_PUB_EAP:
+ version = IKEV2;
+ break;
+ case PROF_V1_PUB:
+ case PROF_V1_XAUTH:
+ case PROF_V1_XAUTH_PSK:
+ case PROF_V1_HYBRID:
+ version = IKEV1;
+ break;
+ }
+
+ local_port = charon->socket->get_port(charon->socket, FALSE);
+ if (local_port != IKEV2_UDP_PORT)
+ {
+ remote_port = IKEV2_NATT_PORT;
+ }
+ ike_cfg = ike_cfg_create(version, TRUE, FALSE, "0.0.0.0", FALSE, local_port,
+ this->host, FALSE, remote_port, FRAGMENTATION_NO, 0);
+ ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
+ peer_cfg = peer_cfg_create("cmd", ike_cfg,
+ CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
+ 36000, 0, /* rekey 10h, reauth none */
+ 600, 600, /* jitter, over 10min */
+ TRUE, FALSE, /* mobike, aggressive */
+ 30, 0, /* DPD delay, timeout */
+ FALSE, NULL, NULL); /* mediation */
+ peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
+
+ return peer_cfg;
+}
+
+/**
+ * Add a single auth cfg of given class to peer cfg
+ */
+static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
+ bool local, auth_class_t class)
+{
+ identification_t *id;
+ auth_cfg_t *auth;
+
+ auth = auth_cfg_create();
+ auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
+ if (local)
+ {
+ id = identification_create_from_string(this->identity);
+ }
+ else
+ {
+ if (this->server)
+ {
+ id = identification_create_from_string(this->server);
+ }
+ else
+ {
+ id = identification_create_from_string(this->host);
+ }
+ }
+ auth->add(auth, AUTH_RULE_IDENTITY, id);
+ peer_cfg->add_auth_cfg(peer_cfg, auth, local);
+}
+
+/**
+ * Attach authentication configs to peer config
+ */
+static bool add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
+{
+ if (this->profile == PROF_UNDEF)
+ {
+ if (this->key_seen)
+ {
+ this->profile = PROF_V2_PUB;
+ }
+ else
+ {
+ this->profile = PROF_V2_EAP;
+ }
+ }
+ switch (this->profile)
+ {
+ case PROF_V2_PUB:
+ case PROF_V2_PUB_EAP:
+ case PROF_V1_PUB:
+ case PROF_V1_XAUTH:
+ if (!this->key_seen)
+ {
+ DBG1(DBG_CFG, "missing private key for profile %N",
+ profile_names, this->profile);
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (this->profile)
+ {
+ case PROF_V2_PUB:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
+ break;
+ case PROF_V2_EAP:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
+ break;
+ case PROF_V2_PUB_EAP:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
+ break;
+ case PROF_V1_PUB:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
+ break;
+ case PROF_V1_XAUTH:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
+ break;
+ case PROF_V1_XAUTH_PSK:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PSK);
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PSK);
+ break;
+ case PROF_V1_HYBRID:
+ add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
+ add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Attach child config to peer config
+ */
+static child_cfg_t* create_child_cfg(private_cmd_connection_t *this)
+{
+ child_cfg_t *child_cfg;
+ traffic_selector_t *ts;
+ lifetime_cfg_t lifetime = {
+ .time = {
+ .life = 10800 /* 3h */,
+ .rekey = 10200 /* 2h50min */,
+ .jitter = 300 /* 5min */
+ }
+ };
+
+ child_cfg = child_cfg_create("cmd", &lifetime,
+ NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
+ ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
+ 0, 0, NULL, NULL, 0);
+ child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
+ while (this->local_ts->remove_first(this->local_ts, (void**)&ts) == SUCCESS)
+ {
+ child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
+ }
+ if (this->remote_ts->get_count(this->remote_ts) == 0)
+ {
+ /* add a 0.0.0.0/0 TS for remote side if none given */
+ ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
+ "0.0.0.0", 0, "255.255.255.255", 65535);
+ this->remote_ts->insert_last(this->remote_ts, ts);
+ }
+ while (this->remote_ts->remove_first(this->remote_ts,
+ (void**)&ts) == SUCCESS)
+ {
+ child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
+ }
+
+ return child_cfg;
+}
+
+/**
+ * Initiate the configured connection
+ */
+static job_requeue_t initiate(private_cmd_connection_t *this)
+{
+ peer_cfg_t *peer_cfg;
+ child_cfg_t *child_cfg;
+
+ if (!this->host)
+ {
+ DBG1(DBG_CFG, "unable to initiate, missing --host option");
+ terminate(this);
+ return JOB_REQUEUE_NONE;
+ }
+ if (!this->identity)
+ {
+ DBG1(DBG_CFG, "unable to initiate, missing --identity option");
+ terminate(this);
+ return JOB_REQUEUE_NONE;
+ }
+
+ peer_cfg = create_peer_cfg(this);
+
+ if (!add_auth_cfgs(this, peer_cfg))
+ {
+ peer_cfg->destroy(peer_cfg);
+ terminate(this);
+ return JOB_REQUEUE_NONE;
+ }
+
+ child_cfg = create_child_cfg(this);
+ peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
+
+ if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
+ controller_cb_empty, NULL, 0) != SUCCESS)
+ {
+ terminate(this);
+ }
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Create a traffic selector from string, add to list
+ */
+static void add_ts(private_cmd_connection_t *this,
+ linked_list_t *list, char *string)
+{
+ traffic_selector_t *ts;
+
+ ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
+ if (!ts)
+ {
+ DBG1(DBG_CFG, "invalid traffic selector: %s", string);
+ exit(1);
+ }
+ list->insert_last(list, ts);
+}
+
+/**
+ * Parse profile name identifier
+ */
+static void set_profile(private_cmd_connection_t *this, char *name)
+{
+ int profile;
+
+ profile = enum_from_name(profile_names, name);
+ if (profile == -1)
+ {
+ DBG1(DBG_CFG, "unknown connection profile: %s", name);
+ exit(1);
+ }
+ this->profile = profile;
+}
+
+METHOD(cmd_connection_t, handle, bool,
+ private_cmd_connection_t *this, cmd_option_type_t opt, char *arg)
+{
+ switch (opt)
+ {
+ case CMD_OPT_HOST:
+ this->host = arg;
+ break;
+ case CMD_OPT_REMOTE_IDENTITY:
+ this->server = arg;
+ break;
+ case CMD_OPT_IDENTITY:
+ this->identity = arg;
+ break;
+ case CMD_OPT_RSA:
+ this->key_seen = TRUE;
+ break;
+ case CMD_OPT_LOCAL_TS:
+ add_ts(this, this->local_ts, arg);
+ break;
+ case CMD_OPT_REMOTE_TS:
+ add_ts(this, this->remote_ts, arg);
+ break;
+ case CMD_OPT_PROFILE:
+ set_profile(this, arg);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+METHOD(cmd_connection_t, destroy, void,
+ private_cmd_connection_t *this)
+{
+ this->local_ts->destroy_offset(this->local_ts,
+ offsetof(traffic_selector_t, destroy));
+ this->remote_ts->destroy_offset(this->remote_ts,
+ offsetof(traffic_selector_t, destroy));
+ free(this);
+}
+
+/**
+ * See header
+ */
+cmd_connection_t *cmd_connection_create()
+{
+ private_cmd_connection_t *this;
+
+ INIT(this,
+ .public = {
+ .handle = _handle,
+ .destroy = _destroy,
+ },
+ .pid = getpid(),
+ .local_ts = linked_list_create(),
+ .remote_ts = linked_list_create(),
+ .profile = PROF_UNDEF,
+ );
+
+ /* always include the virtual IP in traffic selector list */
+ this->local_ts->insert_last(this->local_ts,
+ traffic_selector_create_dynamic(0, 0, 65535));
+
+ /* queue job, gets initiated as soon as we are up and running */
+ lib->processor->queue_job(lib->processor,
+ (job_t*)callback_job_create_with_prio(
+ (callback_job_cb_t)initiate, this, NULL,
+ (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
+
+ return &this->public;
+}
diff --git a/src/charon-cmd/cmd/cmd_connection.h b/src/charon-cmd/cmd/cmd_connection.h
new file mode 100644
index 000000000..0f167bfdc
--- /dev/null
+++ b/src/charon-cmd/cmd/cmd_connection.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 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 cmd_connection cmd_connection
+ * @{ @ingroup cmd
+ */
+
+#ifndef CMD_CONNECTION_H_
+#define CMD_CONNECTION_H_
+
+#include <library.h>
+
+#include "cmd_options.h"
+
+typedef struct cmd_connection_t cmd_connection_t;
+
+/**
+ * Connection definition to construct and initiate.
+ */
+struct cmd_connection_t {
+
+ /**
+ * Handle a command line option.
+ *
+ * @param opt option to handle
+ * @param arg option argument
+ * @return TRUE if option handled
+ */
+ bool (*handle)(cmd_connection_t *this, cmd_option_type_t opt, char *arg);
+
+ /**
+ * Destroy a cmd_connection_t.
+ */
+ void (*destroy)(cmd_connection_t *this);
+};
+
+/**
+ * Create a cmd_connection instance.
+ */
+cmd_connection_t *cmd_connection_create();
+
+#endif /** CMD_CONNECTION_H_ @}*/
diff --git a/src/charon-cmd/cmd/cmd_creds.c b/src/charon-cmd/cmd/cmd_creds.c
new file mode 100644
index 000000000..b70490915
--- /dev/null
+++ b/src/charon-cmd/cmd/cmd_creds.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 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 "cmd_creds.h"
+
+#include <unistd.h>
+
+#include <utils/debug.h>
+#include <credentials/sets/mem_cred.h>
+#include <credentials/sets/callback_cred.h>
+
+typedef struct private_cmd_creds_t private_cmd_creds_t;
+
+/**
+ * Private data of an cmd_creds_t object.
+ */
+struct private_cmd_creds_t {
+
+ /**
+ * Public cmd_creds_t interface.
+ */
+ cmd_creds_t public;
+
+ /**
+ * Reused in-memory credential set
+ */
+ mem_cred_t *creds;
+
+ /**
+ * Callback credential set to get secrets
+ */
+ callback_cred_t *cb;
+
+ /**
+ * Already prompted for password?
+ */
+ bool prompted;
+};
+
+/**
+ * Callback function to prompt for secret
+ */
+static shared_key_t* callback_shared(private_cmd_creds_t *this,
+ shared_key_type_t type,
+ identification_t *me, identification_t *other,
+ id_match_t *match_me, id_match_t *match_other)
+{
+ char *label, *pwd;
+
+ if (this->prompted)
+ {
+ return NULL;
+ }
+ switch (type)
+ {
+ case SHARED_EAP:
+ label = "EAP password: ";
+ break;
+ case SHARED_IKE:
+ label = "Preshared Key: ";
+ break;
+ default:
+ return NULL;
+ }
+ pwd = getpass(label);
+ if (!pwd || strlen(pwd) == 0)
+ {
+ return NULL;
+ }
+ this->prompted = TRUE;
+ *match_me = *match_other = ID_MATCH_PERFECT;
+ return shared_key_create(type, chunk_clone(chunk_from_str(pwd)));
+}
+
+/**
+ * Load a trusted certificate from path
+ */
+static void load_cert(private_cmd_creds_t *this, char *path)
+{
+ certificate_t *cert;
+
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_FROM_FILE, path, BUILD_END);
+ if (!cert)
+ {
+ DBG1(DBG_CFG, "loading certificate from '%s' failed", path);
+ exit(1);
+ }
+ this->creds->add_cert(this->creds, TRUE, cert);
+}
+
+/**
+ * Load a private key of given kind from path
+ */
+static void load_key(private_cmd_creds_t *this, key_type_t type, char *path)
+{
+ private_key_t *privkey;
+
+ privkey = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
+ BUILD_FROM_FILE, path, BUILD_END);
+ if (!privkey)
+ {
+ DBG1(DBG_CFG, "loading %N private key from '%s' failed",
+ key_type_names, type, path);
+ exit(1);
+ }
+ this->creds->add_key(this->creds, privkey);
+}
+
+METHOD(cmd_creds_t, handle, bool,
+ private_cmd_creds_t *this, cmd_option_type_t opt, char *arg)
+{
+ switch (opt)
+ {
+ case CMD_OPT_CERT:
+ load_cert(this, arg);
+ break;
+ case CMD_OPT_RSA:
+ load_key(this, KEY_RSA, arg);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+METHOD(cmd_creds_t, destroy, void,
+ private_cmd_creds_t *this)
+{
+ lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
+ lib->credmgr->remove_set(lib->credmgr, &this->cb->set);
+ this->creds->destroy(this->creds);
+ this->cb->destroy(this->cb);
+ free(this);
+}
+
+/**
+ * See header
+ */
+cmd_creds_t *cmd_creds_create()
+{
+ private_cmd_creds_t *this;
+
+ INIT(this,
+ .public = {
+ .handle = _handle,
+ .destroy = _destroy,
+ },
+ .creds = mem_cred_create(),
+ );
+ this->cb = callback_cred_create_shared((void*)callback_shared, this);
+
+ lib->credmgr->add_set(lib->credmgr, &this->creds->set);
+ lib->credmgr->add_set(lib->credmgr, &this->cb->set);
+
+ return &this->public;
+}
diff --git a/src/charon-cmd/cmd/cmd_creds.h b/src/charon-cmd/cmd/cmd_creds.h
new file mode 100644
index 000000000..053e596a5
--- /dev/null
+++ b/src/charon-cmd/cmd/cmd_creds.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 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 cmd_creds cmd_creds
+ * @{ @ingroup cmd
+ */
+
+#ifndef CMD_CREDS_H_
+#define CMD_CREDS_H_
+
+#include <library.h>
+
+#include "cmd_options.h"
+
+typedef struct cmd_creds_t cmd_creds_t;
+
+/**
+ * Credential backend providing certificates, private keys and shared secrets.
+ */
+struct cmd_creds_t {
+
+ /**
+ * Handle a command line options related to credentials.
+ *
+ * @param opt option to handle
+ * @param arg option argument
+ * @return TRUE if option handled
+ */
+ bool (*handle)(cmd_creds_t *this, cmd_option_type_t opt, char *arg);
+
+ /**
+ * Destroy a cmd_creds_t.
+ */
+ void (*destroy)(cmd_creds_t *this);
+};
+
+/**
+ * Create a cmd_creds instance.
+ */
+cmd_creds_t *cmd_creds_create();
+
+#endif /** CMD_CREDS_H_ @}*/
diff --git a/src/charon-cmd/cmd/cmd_options.c b/src/charon-cmd/cmd/cmd_options.c
new file mode 100644
index 000000000..312d12964
--- /dev/null
+++ b/src/charon-cmd/cmd/cmd_options.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 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 "cmd_options.h"
+
+#include <getopt.h>
+
+/**
+ * See header.
+ */
+cmd_option_t cmd_options[CMD_OPT_COUNT] = {
+ { CMD_OPT_HELP, "help", no_argument, "",
+ "print this usage information and exit" },
+ { CMD_OPT_VERSION, "version", no_argument, "",
+ "show version information and exit" },
+ { CMD_OPT_HOST, "host", required_argument, "hostname",
+ "DNS name or address to connect to" },
+ { CMD_OPT_IDENTITY, "identity", required_argument, "identity",
+ "identity the client uses for the IKE exchange" },
+ { CMD_OPT_REMOTE_IDENTITY, "remote-identity", required_argument, "identity",
+ "server identity to expect, defaults to host" },
+ { CMD_OPT_CERT, "cert", required_argument, "path",
+ "trusted certificate, for authentication or trust chain validation" },
+ { CMD_OPT_RSA, "rsa", required_argument, "path",
+ "RSA private key to use for authentication" },
+ { CMD_OPT_LOCAL_TS, "local-ts", required_argument, "subnet",
+ "additional traffic selector to propose for our side" },
+ { CMD_OPT_REMOTE_TS, "remote-ts", required_argument, "subnet",
+ "remote traffic selector to propose for remote side" },
+ { CMD_OPT_PROFILE, "profile", required_argument, "name",
+ "authentication profile to use, where name is one of:", {
+ "ikev2-pub: IKEv2 with public key client authentication",
+ "ikev2-eap: IKEv2 with client EAP",
+ "ikev2-pub-eap: IKEv2 with public key client authentication + client EAP",
+ "ikev1-pub: IKEv1 public key authentication",
+ "ikev1-xauth: IKEv1 public key authentication + initiator XAuth",
+ "ikev1-xauth-psk: IKEv1 PSK authentication + initiator XAuth (INSECURE!)",
+ "ikev1-hybrid: IKEv1 public key responder only + initiator XAuth",
+ }},
+};
diff --git a/src/charon-cmd/cmd/cmd_options.h b/src/charon-cmd/cmd/cmd_options.h
new file mode 100644
index 000000000..addbb50d8
--- /dev/null
+++ b/src/charon-cmd/cmd/cmd_options.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 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 cmd_option cmd_option
+ * @{ @ingroup cmd
+ */
+
+#ifndef CMD_OPTION_H_
+#define CMD_OPTION_H_
+
+typedef struct cmd_option_t cmd_option_t;
+typedef enum cmd_option_type_t cmd_option_type_t;
+
+/**
+ * Command line options
+ */
+enum cmd_option_type_t {
+ CMD_OPT_HELP,
+ CMD_OPT_VERSION,
+ CMD_OPT_HOST,
+ CMD_OPT_IDENTITY,
+ CMD_OPT_REMOTE_IDENTITY,
+ CMD_OPT_CERT,
+ CMD_OPT_RSA,
+ CMD_OPT_LOCAL_TS,
+ CMD_OPT_REMOTE_TS,
+ CMD_OPT_PROFILE,
+
+ CMD_OPT_COUNT
+};
+
+/**
+ * Command line arguments, similar to "struct option", but with descriptions
+ */
+struct cmd_option_t {
+ /** option identifier */
+ cmd_option_type_t id;
+ /** long option name */
+ const char *name;
+ /** takes argument */
+ int has_arg;
+ /** decription of argument */
+ const char *arg;
+ /** short description to option */
+ const char *desc;
+ /** additional description lines */
+ const char *lines[8];
+};
+
+/**
+ * Registered CMD options.
+ */
+extern cmd_option_t cmd_options[CMD_OPT_COUNT];
+
+#endif /** CMD_OPTION_H_ @}*/
diff --git a/src/checksum/Makefile.am b/src/checksum/Makefile.am
index 9f6945087..aabd96e77 100644
--- a/src/checksum/Makefile.am
+++ b/src/checksum/Makefile.am
@@ -84,6 +84,10 @@ if !MONOLITHIC
endif
endif
+if USE_CMD
+ exes += $(top_builddir)/src/charon-cmd/.libs/charon-cmd
+endif
+
if USE_TOOLS
exes += $(top_builddir)/src/openac/.libs/openac
exes += $(top_builddir)/src/pki/.libs/pki
diff --git a/src/libcharon/control/controller.c b/src/libcharon/control/controller.c
index 0ee99c4b7..c546da544 100644
--- a/src/libcharon/control/controller.c
+++ b/src/libcharon/control/controller.c
@@ -412,6 +412,7 @@ METHOD(controller_t, initiate, status_t,
.refcount = 1,
);
job->listener.logger.listener = &job->listener;
+ thread_cleanup_push((void*)destroy_job, job);
if (callback == NULL)
{
@@ -425,7 +426,7 @@ METHOD(controller_t, initiate, status_t,
}
}
status = job->listener.status;
- destroy_job(job);
+ thread_cleanup_pop(TRUE);
return status;
}
@@ -500,6 +501,7 @@ METHOD(controller_t, terminate_ike, status_t,
.refcount = 1,
);
job->listener.logger.listener = &job->listener;
+ thread_cleanup_push((void*)destroy_job, job);
if (callback == NULL)
{
@@ -513,7 +515,7 @@ METHOD(controller_t, terminate_ike, status_t,
}
}
status = job->listener.status;
- destroy_job(job);
+ thread_cleanup_pop(TRUE);
return status;
}
@@ -615,6 +617,7 @@ METHOD(controller_t, terminate_child, status_t,
.refcount = 1,
);
job->listener.logger.listener = &job->listener;
+ thread_cleanup_push((void*)destroy_job, job);
if (callback == NULL)
{
@@ -628,7 +631,7 @@ METHOD(controller_t, terminate_child, status_t,
}
}
status = job->listener.status;
- destroy_job(job);
+ thread_cleanup_pop(TRUE);
return status;
}
diff --git a/src/libcharon/encoding/payloads/proposal_substructure.c b/src/libcharon/encoding/payloads/proposal_substructure.c
index ae0fce991..3cf22aefd 100644
--- a/src/libcharon/encoding/payloads/proposal_substructure.c
+++ b/src/libcharon/encoding/payloads/proposal_substructure.c
@@ -1224,7 +1224,7 @@ static void set_from_proposal_v1_ike(private_proposal_substructure_t *this,
number, IKEV1_TRANSID_KEY_IKE);
enumerator = proposal->create_enumerator(proposal, ENCRYPTION_ALGORITHM);
- if (enumerator->enumerate(enumerator, &alg, &key_size))
+ while (enumerator->enumerate(enumerator, &alg, &key_size))
{
alg = get_ikev1_from_alg(ENCRYPTION_ALGORITHM, alg);
if (alg)
@@ -1238,13 +1238,14 @@ static void set_from_proposal_v1_ike(private_proposal_substructure_t *this,
transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
TATTR_PH1_KEY_LENGTH, key_size));
}
+ break;
}
}
enumerator->destroy(enumerator);
/* encode the integrity algorithm as hash and assume use the same PRF */
enumerator = proposal->create_enumerator(proposal, INTEGRITY_ALGORITHM);
- if (enumerator->enumerate(enumerator, &alg, &key_size))
+ while (enumerator->enumerate(enumerator, &alg, &key_size))
{
alg = get_ikev1_from_alg(INTEGRITY_ALGORITHM, alg);
if (alg)
@@ -1252,6 +1253,7 @@ static void set_from_proposal_v1_ike(private_proposal_substructure_t *this,
transform->add_transform_attribute(transform,
transform_attribute_create_value(TRANSFORM_ATTRIBUTE_V1,
TATTR_PH1_HASH_ALGORITHM, alg));
+ break;
}
}
enumerator->destroy(enumerator);
diff --git a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
index a5e919348..b7c73945d 100644
--- a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
+++ b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
@@ -326,13 +326,60 @@ METHOD(socket_t, receiver, status_t,
}
/**
+ * Get the port allocated dynamically using bind()
+ */
+static bool get_dynamic_port(int fd, int family, u_int16_t *port)
+{
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr s;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
+ socklen_t addrlen;
+
+ addrlen = sizeof(addr);
+ if (getsockname(fd, &addr.s, &addrlen) != 0)
+ {
+ DBG1(DBG_NET, "unable to getsockname: %s", strerror(errno));
+ return FALSE;
+ }
+ switch (family)
+ {
+ case AF_INET:
+ if (addrlen != sizeof(addr.sin) || addr.sin.sin_family != family)
+ {
+ break;
+ }
+ *port = ntohs(addr.sin.sin_port);
+ return TRUE;
+ case AF_INET6:
+ if (addrlen != sizeof(addr.sin6) || addr.sin6.sin6_family != family)
+ {
+ break;
+ }
+ *port = ntohs(addr.sin6.sin6_port);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ DBG1(DBG_NET, "received invalid getsockname() result");
+ return FALSE;
+}
+
+/**
* open a socket to send and receive packets
*/
static int open_socket(private_socket_dynamic_socket_t *this,
- int family, u_int16_t port)
+ int family, u_int16_t *port)
{
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr s;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
int on = TRUE;
- struct sockaddr_storage addr;
socklen_t addrlen;
u_int sol, pktinfo = 0;
int fd;
@@ -342,27 +389,21 @@ static int open_socket(private_socket_dynamic_socket_t *this,
switch (family)
{
case AF_INET:
- {
- struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = INADDR_ANY;
- sin->sin_port = htons(port);
- addrlen = sizeof(struct sockaddr_in);
+ addr.sin.sin_family = AF_INET;
+ addr.sin.sin_addr.s_addr = INADDR_ANY;
+ addr.sin.sin_port = htons(*port);
+ addrlen = sizeof(addr.sin);
sol = SOL_IP;
pktinfo = IP_PKTINFO;
break;
- }
case AF_INET6:
- {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
- sin6->sin6_family = AF_INET6;
- memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
- sin6->sin6_port = htons(port);
- addrlen = sizeof(struct sockaddr_in6);
+ addr.sin6.sin6_family = AF_INET6;
+ memset(&addr.sin6.sin6_addr, 0, sizeof(addr.sin6));
+ addr.sin6.sin6_port = htons(*port);
+ addrlen = sizeof(addr.sin6);
sol = SOL_IPV6;
pktinfo = IPV6_RECVPKTINFO;
break;
- }
default:
return 0;
}
@@ -380,13 +421,17 @@ static int open_socket(private_socket_dynamic_socket_t *this,
return 0;
}
- /* bind the socket */
- if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0)
+ if (bind(fd, &addr.s, addrlen) < 0)
{
DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
close(fd);
return 0;
}
+ if (*port == 0 && !get_dynamic_port(fd, family, port))
+ {
+ close(fd);
+ return 0;
+ }
/* get additional packet info on receive */
if (setsockopt(fd, sol, pktinfo, &on, sizeof(on)) < 0)
@@ -404,16 +449,41 @@ static int open_socket(private_socket_dynamic_socket_t *this,
/* enable UDP decapsulation on each socket */
if (!hydra->kernel_interface->enable_udp_decap(hydra->kernel_interface,
- fd, family, port))
+ fd, family, *port))
{
DBG1(DBG_NET, "enabling UDP decapsulation for %s on port %d failed",
- family == AF_INET ? "IPv4" : "IPv6", port);
+ family == AF_INET ? "IPv4" : "IPv6", *port);
}
return fd;
}
/**
+ * Get the first usable socket for an address family
+ */
+static dynsock_t *get_any_socket(private_socket_dynamic_socket_t *this,
+ int family)
+{
+ dynsock_t *key, *value, *found = NULL;
+ enumerator_t *enumerator;
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->sockets->create_enumerator(this->sockets);
+ while (enumerator->enumerate(enumerator, &key, &value))
+ {
+ if (value->family == family)
+ {
+ found = value;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+
+ return found;
+}
+
+/**
* Find/Create a socket to send from host
*/
static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
@@ -433,7 +503,15 @@ static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
{
return skt;
}
- fd = open_socket(this, family, port);
+ if (!port)
+ {
+ skt = get_any_socket(this, family);
+ if (skt)
+ {
+ return skt;
+ }
+ }
+ fd = open_socket(this, family, &port);
if (!fd)
{
return NULL;
@@ -457,7 +535,7 @@ METHOD(socket_t, sender, status_t,
{
dynsock_t *skt;
host_t *src, *dst;
- int port, family;
+ int family;
ssize_t len;
chunk_t data;
struct msghdr msg;
@@ -467,9 +545,7 @@ METHOD(socket_t, sender, status_t,
src = packet->get_source(packet);
dst = packet->get_destination(packet);
family = src->get_family(src);
- port = src->get_port(src);
- port = port ?: CHARON_UDP_PORT;
- skt = find_socket(this, family, port);
+ skt = find_socket(this, family, src->get_port(src));
if (!skt)
{
return FAILED;
@@ -597,4 +673,3 @@ socket_dynamic_socket_t *socket_dynamic_socket_create()
return &this->public;
}
-
diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c
index 712ea6ee2..809ca10ab 100644
--- a/src/libstrongswan/utils/settings.c
+++ b/src/libstrongswan/utils/settings.c
@@ -644,6 +644,26 @@ METHOD(settings_t, set_time, void,
va_end(args);
}
+METHOD(settings_t, set_default_str, bool,
+ private_settings_t *this, char *key, char *value, ...)
+{
+ char *old;
+ va_list args;
+
+ va_start(args, value);
+ old = find_value(this, this->top, key, args);
+ va_end(args);
+
+ if (!old)
+ {
+ va_start(args, value);
+ set_value(this, this->top, key, args, value);
+ va_end(args);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
* Enumerate section names, not sections
*/
@@ -1209,6 +1229,7 @@ settings_t *settings_create(char *file)
.set_double = _set_double,
.set_time = _set_time,
.set_bool = _set_bool,
+ .set_default_str = _set_default_str,
.create_section_enumerator = _create_section_enumerator,
.create_key_value_enumerator = _create_key_value_enumerator,
.load_files = _load_files,
diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h
index a861325f5..df0c534e9 100644
--- a/src/libstrongswan/utils/settings.h
+++ b/src/libstrongswan/utils/settings.h
@@ -239,6 +239,16 @@ struct settings_t {
void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
/**
+ * Set a default for string value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value to set if unconfigured
+ * @param ... argument list for key
+ * @return TRUE if a new default value for key has been set
+ */
+ bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
+
+ /**
* Create an enumerator over subsection names of a section.
*
* @param section section including parents, printf style format