diff options
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/charon-svc/Makefile.am | 16 | ||||
-rw-r--r-- | src/charon-svc/charon-svc.c | 264 |
4 files changed, 290 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index e3e4f881a..4eda8eec4 100644 --- a/configure.ac +++ b/configure.ac @@ -266,6 +266,7 @@ ARG_ENABL_SET([medcli], [enable mediation client configuration database ARG_ENABL_SET([medsrv], [enable mediation server web frontend and daemon plugin.]) ARG_ENABL_SET([nm], [enable NetworkManager backend.]) ARG_DISBL_SET([scripts], [disable additional utilities (found in directory scripts).]) +ARG_ENABL_SET([svc], [enable charon Windows service.]) ARG_ENABL_SET([swanctl], [enable swanctl configuration and control tool.]) ARG_ENABL_SET([tkm], [enable Trusted Key Manager support.]) ARG_DISBL_SET([tools], [disable additional utilities (scepclient and pki).]) @@ -1400,9 +1401,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 -o x$cmd = xtrue -o x$tls = xtrue -o x$tnc_tnccs = xtrue -o x$aikgen = 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_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 -o x$tls = xtrue -o x$tnc_tnccs = xtrue -o x$aikgen = xtrue -o x$svc = xtrue) +AM_CONDITIONAL(USE_LIBHYDRA, test x$charon = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$svc = 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 -o x$svc = 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) @@ -1426,6 +1427,7 @@ AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue) AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue) AM_CONDITIONAL(USE_AIKGEN, test x$aikgen = xtrue) AM_CONDITIONAL(USE_SWANCTL, test x$swanctl = xtrue) +AM_CONDITIONAL(USE_SVC, test x$svc = xtrue) # ======================== # set global definitions @@ -1565,6 +1567,7 @@ AC_CONFIG_FILES([ src/charon-nm/Makefile src/charon-tkm/Makefile src/charon-cmd/Makefile + src/charon-svc/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 38e4b834d..89c059255 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -116,6 +116,10 @@ if USE_CMD SUBDIRS += charon-cmd endif +if USE_SVC + SUBDIRS += charon-svc +endif + if USE_LIBPTTLS SUBDIRS += pt-tls-client endif diff --git a/src/charon-svc/Makefile.am b/src/charon-svc/Makefile.am new file mode 100644 index 000000000..ecccf02f5 --- /dev/null +++ b/src/charon-svc/Makefile.am @@ -0,0 +1,16 @@ +bin_PROGRAMS = charon-svc + +charon_svc_SOURCES = charon-svc.c + +charon-svc.o : $(top_builddir)/config.status + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon \ + -DPLUGINS=\""${charon_plugins}\"" + +charon_svc_LDADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libhydra/libhydra.la \ + $(top_builddir)/src/libcharon/libcharon.la diff --git a/src/charon-svc/charon-svc.c b/src/charon-svc/charon-svc.c new file mode 100644 index 000000000..3f4b80dce --- /dev/null +++ b/src/charon-svc/charon-svc.c @@ -0,0 +1,264 @@ +/* + * 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 <library.h> +#include <hydra.h> +#include <daemon.h> + +#include <utils/backtrace.h> +#include <threading/thread.h> + +/** + * The name of our service, both internal and external + */ +#define SERVICE_NAME "charon-svc" + +/** + * Current service status + */ +static SERVICE_STATUS status; + +/** + * Handle for service status + */ +static SERVICE_STATUS_HANDLE handle; + +/** + * Wait event for main thread + */ +static HANDLE event; + +/** + * 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); + } +} + +/** + * Log strongSwan/Windows version during startup + */ +static void print_version() +{ + OSVERSIONINFOEX osvie; + + memset(&osvie, 0, sizeof(osvie)); + osvie.dwOSVersionInfoSize = sizeof(osvie); + + if (GetVersionEx((LPOSVERSIONINFO)&osvie)) + { + DBG1(DBG_DMN, "Starting IKE service %s (strongSwan %s, " + "Windows %s %d.%d.%d (SP %d.%d)", SERVICE_NAME, VERSION, + osvie.wProductType == VER_NT_WORKSTATION ? "Client" : "Server", + osvie.dwMajorVersion, osvie.dwMinorVersion, osvie.dwBuildNumber, + osvie.wServicePackMajor, osvie.wServicePackMinor); + } +} + +/** + * Update service state to SCM, increase check point if state didn't change + */ +static void update_status(DWORD state) +{ + if (state == status.dwCurrentState) + { + status.dwCheckPoint++; + } + else + { + status.dwCheckPoint = 0; + } + status.dwCurrentState = state; + if (handle) + { + SetServiceStatus(handle, &status); + } +} + +/** + * Initialize and run charon + */ +static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv) +{ + level_t levels[DBG_MAX]; + int i; + + for (i = 0; i < DBG_MAX; i++) + { + levels[i] = LEVEL_CTRL; + } + + update_status(SERVICE_START_PENDING); + event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (event) + { + update_status(SERVICE_START_PENDING); + if (library_init(NULL, SERVICE_NAME)) + { + update_status(SERVICE_START_PENDING); + if (libhydra_init()) + { + update_status(SERVICE_START_PENDING); + if (libcharon_init()) + { + charon->load_loggers(charon, levels, TRUE); + print_version(); + update_status(SERVICE_START_PENDING); + if (charon->initialize(charon, PLUGINS)) + { + update_status(SERVICE_START_PENDING); + lib->plugins->status(lib->plugins, LEVEL_CTRL); + + charon->start(charon); + + status.dwWin32ExitCode = 0; + update_status(SERVICE_RUNNING); + + /* main thread goes to sleep */ + WaitForSingleObjectEx(event, INFINITE, TRUE); + } + update_status(SERVICE_STOP_PENDING); + libcharon_deinit(); + } + update_status(SERVICE_STOP_PENDING); + libhydra_deinit(); + } + update_status(SERVICE_STOP_PENDING); + library_deinit(); + } + update_status(SERVICE_STOP_PENDING); + CloseHandle(event); + } + update_status(SERVICE_STOPPED); +} + +/** + * Control handler for console + */ +static BOOL console_handler(DWORD dwCtrlType) +{ + switch (dwCtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + DBG1(DBG_DMN, "application is stopping, cleaning up"); + charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, dwCtrlType); + /* signal main thread to clean up */ + SetEvent(event); + return TRUE; + default: + return FALSE; + } +} + +/** + * Main routine when running from console + */ +static void console_main(DWORD dwArgc, LPTSTR *lpszArgv) +{ + status.dwWin32ExitCode = 1; + + if (SetConsoleCtrlHandler(console_handler, TRUE)) + { + init_and_run(dwArgc, lpszArgv); + SetConsoleCtrlHandler(console_handler, FALSE); + } +} + +/** + * Service handler function + */ +static DWORD service_handler(DWORD dwControl, DWORD dwEventType, + LPVOID lpEventData, LPVOID lpContext) +{ + switch (dwControl) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + DBG1(DBG_DMN, "service is stopping, cleaning up"); + charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, dwControl); + /* signal main thread to clean up */ + SetEvent(event); + return NO_ERROR; + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } +} + +/** + * Service main routine when running as service + */ +static void service_main(DWORD dwArgc, LPTSTR *lpszArgv) +{ + memset(&status, 0, sizeof(status)); + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = 1; + + handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, service_handler, NULL); + if (handle) + { + init_and_run(dwArgc, lpszArgv); + } +} + +/** + * Main function, starts the service + */ +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY services[] = { + { + .lpServiceName = SERVICE_NAME, + .lpServiceProc = service_main, + }, + { NULL, NULL }, + }; + DWORD err; + + dbg = dbg_stderr; + + if (!StartServiceCtrlDispatcher(services)) + { + err = GetLastError(); + if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) + { + console_main(argc, argv); + } + else + { + return 2; + } + } + return status.dwWin32ExitCode; +} |