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.
This commit is contained in:
Martin Willi 2013-05-06 16:01:25 +02:00
commit 922e2d1d62
17 changed files with 1454 additions and 80 deletions

View 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

View 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 :

1
src/charon-cmd/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
charon-cmd

View File

@ -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)

380
src/charon-cmd/charon-cmd.c Normal file
View File

@ -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();
}

View File

@ -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;
}

View File

@ -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_ @}*/

View File

@ -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;
}

View File

@ -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_ @}*/

View File

@ -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",
}},
};

View File

@ -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_ @}*/

View 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

View 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;
}

View 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);

View File

@ -325,14 +325,61 @@ METHOD(socket_t, receiver, status_t,
return FAILED;
}
/**
* 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,15 +449,40 @@ 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
*/
@ -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;
}

View 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,

View File

@ -238,6 +238,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.
*