diff options
author | Martin Willi <martin@revosec.ch> | 2013-05-06 16:01:25 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2013-05-06 16:01:25 +0200 |
commit | 922e2d1d62541454be7d84c7551a29a9948f853c (patch) | |
tree | a31602816effaafe08e52ab9a46f6cbbc58260a2 | |
parent | b4e9f74e428ab0f3026df4175d133b81b16c9d56 (diff) | |
parent | 68fc0fe32eb3d7d89c9639b44903b98a1e073a05 (diff) | |
download | strongswan-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.in | 103 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/charon-cmd/.gitignore | 1 | ||||
-rw-r--r-- | src/charon-cmd/Makefile.am | 25 | ||||
-rw-r--r-- | src/charon-cmd/charon-cmd.c | 380 | ||||
-rw-r--r-- | src/charon-cmd/cmd/cmd_connection.c | 445 | ||||
-rw-r--r-- | src/charon-cmd/cmd/cmd_connection.h | 55 | ||||
-rw-r--r-- | src/charon-cmd/cmd/cmd_creds.c | 169 | ||||
-rw-r--r-- | src/charon-cmd/cmd/cmd_creds.h | 55 | ||||
-rw-r--r-- | src/charon-cmd/cmd/cmd_options.c | 52 | ||||
-rw-r--r-- | src/charon-cmd/cmd/cmd_options.h | 68 | ||||
-rw-r--r-- | src/checksum/Makefile.am | 4 | ||||
-rw-r--r-- | src/libcharon/control/controller.c | 9 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/proposal_substructure.c | 6 | ||||
-rw-r--r-- | src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c | 127 | ||||
-rw-r--r-- | src/libstrongswan/utils/settings.c | 21 | ||||
-rw-r--r-- | src/libstrongswan/utils/settings.h | 10 |
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 |