Merge branch 'charon-cmd'
authorMartin Willi <martin@revosec.ch>
Mon, 6 May 2013 14:01:25 +0000 (16:01 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 6 May 2013 14:01:25 +0000 (16:01 +0200)
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.

17 files changed:
configure.in
src/Makefile.am
src/charon-cmd/.gitignore [new file with mode: 0644]
src/charon-cmd/Makefile.am [new file with mode: 0644]
src/charon-cmd/charon-cmd.c [new file with mode: 0644]
src/charon-cmd/cmd/cmd_connection.c [new file with mode: 0644]
src/charon-cmd/cmd/cmd_connection.h [new file with mode: 0644]
src/charon-cmd/cmd/cmd_creds.c [new file with mode: 0644]
src/charon-cmd/cmd/cmd_creds.h [new file with mode: 0644]
src/charon-cmd/cmd/cmd_options.c [new file with mode: 0644]
src/charon-cmd/cmd/cmd_options.h [new file with mode: 0644]
src/checksum/Makefile.am
src/libcharon/control/controller.c
src/libcharon/encoding/payloads/proposal_substructure.c
src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
src/libstrongswan/utils/settings.c
src/libstrongswan/utils/settings.h

index 311b15c..0682975 100644 (file)
@@ -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
index 07953b0..47299b0 100644 (file)
@@ -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 (file)
index 0000000..c02dfba
--- /dev/null
@@ -0,0 +1 @@
+charon-cmd
diff --git a/src/charon-cmd/Makefile.am b/src/charon-cmd/Makefile.am
new file mode 100644 (file)
index 0000000..2c9f1ba
--- /dev/null
@@ -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 (file)
index 0000000..5f27255
--- /dev/null
@@ -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 (file)
index 0000000..965b72b
--- /dev/null
@@ -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 (file)
index 0000000..0f167bf
--- /dev/null
@@ -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 (file)
index 0000000..b704909
--- /dev/null
@@ -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 (file)
index 0000000..053e596
--- /dev/null
@@ -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 (file)
index 0000000..312d129
--- /dev/null
@@ -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 (file)
index 0000000..addbb50
--- /dev/null
@@ -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_ @}*/
index 9f69450..aabd96e 100644 (file)
@@ -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
index 0ee99c4..c546da5 100644 (file)
@@ -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;
 }
 
index ae0fce9..3cf22ae 100644 (file)
@@ -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);
index a5e9193..b7c7394 100644 (file)
@@ -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;
 }
-
index 712ea6e..809ca10 100644 (file)
@@ -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,
index a861325..df0c534 100644 (file)
@@ -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