While there is a status message sent to systemd (can be seen e.g. in systemctl status), the version etc. is currently not logged to the journal, syslog or any log files.
420 lines
9.1 KiB
C
420 lines
9.1 KiB
C
/*
|
|
* Copyright (C) 2006-2018 Tobias Brunner
|
|
* Copyright (C) 2005-2014 Martin Willi
|
|
* Copyright (C) 2006 Daniel Roethlisberger
|
|
* Copyright (C) 2005 Jan Hutter
|
|
*
|
|
* Copyright (C) secunet Security Networks 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 <signal.h>
|
|
#include <stdio.h>
|
|
#include <pthread.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/utsname.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
/* won't make sense from our logging hook */
|
|
#define SD_JOURNAL_SUPPRESS_LOCATION
|
|
#include <systemd/sd-daemon.h>
|
|
#include <systemd/sd-journal.h>
|
|
|
|
#include <daemon.h>
|
|
|
|
#include <library.h>
|
|
#include <utils/backtrace.h>
|
|
#include <threading/thread.h>
|
|
#include <threading/rwlock.h>
|
|
|
|
/**
|
|
* Default user and group
|
|
*/
|
|
#ifndef IPSEC_USER
|
|
#define IPSEC_USER NULL
|
|
#endif
|
|
|
|
#ifndef IPSEC_GROUP
|
|
#define IPSEC_GROUP NULL
|
|
#endif
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
typedef struct journal_logger_t journal_logger_t;
|
|
|
|
/**
|
|
* Logger implementation using systemd-journal
|
|
*/
|
|
struct journal_logger_t {
|
|
|
|
/**
|
|
* Public interface
|
|
*/
|
|
custom_logger_t public;
|
|
|
|
/**
|
|
* Configured loglevels
|
|
*/
|
|
level_t levels[DBG_MAX];
|
|
|
|
/**
|
|
* Lock for levels
|
|
*/
|
|
rwlock_t *lock;
|
|
};
|
|
|
|
METHOD(logger_t, vlog, void,
|
|
journal_logger_t *this, debug_t group, level_t level, int thread,
|
|
ike_sa_t *ike_sa, const char *fmt, va_list args)
|
|
{
|
|
char buf[4096], *msg = buf;
|
|
ssize_t len;
|
|
va_list copy;
|
|
|
|
va_copy(copy, args);
|
|
len = vsnprintf(msg, sizeof(buf), fmt, copy);
|
|
va_end(copy);
|
|
|
|
if (len >= sizeof(buf))
|
|
{
|
|
len++;
|
|
msg = malloc(len);
|
|
va_copy(copy, args);
|
|
len = vsnprintf(msg, len, fmt, copy);
|
|
va_end(copy);
|
|
}
|
|
if (len > 0)
|
|
{
|
|
char unique[64] = "", name[256] = "";
|
|
int priority;
|
|
|
|
if (ike_sa)
|
|
{
|
|
snprintf(unique, sizeof(unique), "IKE_SA_UNIQUE_ID=%u",
|
|
ike_sa->get_unique_id(ike_sa));
|
|
if (ike_sa->get_peer_cfg(ike_sa))
|
|
{
|
|
snprintf(name, sizeof(name), "IKE_SA_NAME=%s",
|
|
ike_sa->get_name(ike_sa));
|
|
}
|
|
}
|
|
switch (level)
|
|
{
|
|
case LEVEL_AUDIT:
|
|
priority = LOG_NOTICE;
|
|
break;
|
|
case LEVEL_CTRL:
|
|
priority = LOG_INFO;
|
|
break;
|
|
default:
|
|
priority = LOG_DEBUG;
|
|
break;
|
|
}
|
|
sd_journal_send(
|
|
"MESSAGE=%s", msg,
|
|
"MESSAGE_ID=57d2708c-d607-43bd-8c39-66bf%.8x",
|
|
chunk_hash_static(chunk_from_str((char*)fmt)),
|
|
"PRIORITY=%d", priority,
|
|
"GROUP=%N", debug_names, group,
|
|
"LEVEL=%d", level,
|
|
"THREAD=%d", thread,
|
|
unique[0] ? unique : NULL,
|
|
name[0] ? name : NULL,
|
|
NULL);
|
|
}
|
|
if (msg != buf)
|
|
{
|
|
free(msg);
|
|
}
|
|
}
|
|
|
|
METHOD(logger_t, get_level, level_t,
|
|
journal_logger_t *this, debug_t group)
|
|
{
|
|
level_t level;
|
|
|
|
this->lock->read_lock(this->lock);
|
|
level = this->levels[group];
|
|
this->lock->unlock(this->lock);
|
|
|
|
return level;
|
|
}
|
|
|
|
METHOD(custom_logger_t, set_level, void,
|
|
journal_logger_t *this, debug_t group, level_t level)
|
|
{
|
|
this->lock->write_lock(this->lock);
|
|
this->levels[group] = level;
|
|
this->lock->unlock(this->lock);
|
|
}
|
|
|
|
METHOD(custom_logger_t, logger_destroy, void,
|
|
journal_logger_t *this)
|
|
{
|
|
this->lock->destroy(this->lock);
|
|
free(this);
|
|
}
|
|
|
|
static custom_logger_t *journal_logger_create(const char *name)
|
|
{
|
|
journal_logger_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.logger = {
|
|
.vlog = _vlog,
|
|
.get_level = _get_level,
|
|
},
|
|
.set_level = _set_level,
|
|
.destroy = _logger_destroy,
|
|
},
|
|
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
|
);
|
|
return &this->public;
|
|
}
|
|
|
|
/**
|
|
* Run the daemon and handle unix signals
|
|
*/
|
|
static int run()
|
|
{
|
|
sigset_t set;
|
|
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGHUP);
|
|
sigaddset(&set, SIGTERM);
|
|
sigprocmask(SIG_BLOCK, &set, NULL);
|
|
|
|
sd_notify(0, "READY=1\n");
|
|
|
|
while (TRUE)
|
|
{
|
|
int sig;
|
|
|
|
sig = sigwaitinfo(&set, NULL);
|
|
if (sig == -1)
|
|
{
|
|
if (errno == EINTR)
|
|
{ /* ignore signals we didn't wait for */
|
|
continue;
|
|
}
|
|
DBG1(DBG_DMN, "waiting for signal failed: %s", strerror(errno));
|
|
return SS_RC_INITIALIZATION_FAILED;
|
|
}
|
|
switch (sig)
|
|
{
|
|
case SIGHUP:
|
|
{
|
|
DBG1(DBG_DMN, "signal of type SIGHUP received. Reloading "
|
|
"configuration");
|
|
if (lib->settings->load_files(lib->settings, lib->conf, FALSE))
|
|
{
|
|
charon->load_loggers(charon);
|
|
lib->plugins->reload(lib->plugins, NULL);
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_DMN, "reloading config failed, keeping old");
|
|
}
|
|
break;
|
|
}
|
|
case SIGTERM:
|
|
{
|
|
DBG1(DBG_DMN, "SIGTERM received, shutting down");
|
|
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lookup UID and GID
|
|
*/
|
|
static bool lookup_uid_gid()
|
|
{
|
|
char *name;
|
|
|
|
name = lib->settings->get_str(lib->settings, "%s.user", IPSEC_USER,
|
|
lib->ns);
|
|
if (name && !lib->caps->resolve_uid(lib->caps, name))
|
|
{
|
|
return FALSE;
|
|
}
|
|
name = lib->settings->get_str(lib->settings, "%s.group", IPSEC_GROUP,
|
|
lib->ns);
|
|
if (name && !lib->caps->resolve_gid(lib->caps, name))
|
|
{
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef DISABLE_SIGNAL_HANDLER
|
|
/**
|
|
* 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, NULL, TRUE);
|
|
backtrace->log(backtrace, stderr, TRUE);
|
|
backtrace->destroy(backtrace);
|
|
|
|
DBG1(DBG_DMN, "killing ourself, received critical signal");
|
|
abort();
|
|
}
|
|
#endif /* DISABLE_SIGNAL_HANDLER */
|
|
|
|
/**
|
|
* Add namespace alias
|
|
*/
|
|
static void __attribute__ ((constructor))register_namespace()
|
|
{
|
|
/* inherit settings from charon */
|
|
library_add_namespace("charon");
|
|
}
|
|
|
|
/**
|
|
* Register journal logger
|
|
*/
|
|
static void __attribute__ ((constructor))register_logger()
|
|
{
|
|
register_custom_logger("journal", journal_logger_create);
|
|
}
|
|
|
|
/**
|
|
* Main function, starts the daemon.
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct sigaction action;
|
|
struct utsname utsname;
|
|
int status = SS_RC_INITIALIZATION_FAILED;
|
|
|
|
dbg = dbg_stderr;
|
|
|
|
if (uname(&utsname) != 0)
|
|
{
|
|
memset(&utsname, 0, sizeof(utsname));
|
|
}
|
|
|
|
sd_notifyf(0, "STATUS=Starting charon-systemd, strongSwan %s, %s %s, %s",
|
|
VERSION, utsname.sysname, utsname.release, utsname.machine);
|
|
|
|
atexit(library_deinit);
|
|
if (!library_init(NULL, "charon-systemd"))
|
|
{
|
|
sd_notifyf(0, "STATUS=libstrongswan initialization failed");
|
|
return SS_RC_INITIALIZATION_FAILED;
|
|
}
|
|
if (lib->integrity &&
|
|
!lib->integrity->check_file(lib->integrity, "charon-systemd", argv[0]))
|
|
{
|
|
sd_notifyf(0, "STATUS=integrity check of charon-systemd failed");
|
|
return SS_RC_INITIALIZATION_FAILED;
|
|
}
|
|
if (!libcharon_init())
|
|
{
|
|
sd_notifyf(0, "STATUS=libcharon initialization failed");
|
|
goto error;
|
|
}
|
|
if (!lookup_uid_gid())
|
|
{
|
|
sd_notifyf(0, "STATUS=unknown uid/gid");
|
|
goto error;
|
|
}
|
|
/* we registered the journal logger as custom logger, which gets its
|
|
* settings from <ns>.customlog.journal, let it fallback to <ns>.journal */
|
|
lib->settings->add_fallback(lib->settings, "%s.customlog.journal",
|
|
"%s.journal", lib->ns);
|
|
/* load the journal logger by default */
|
|
lib->settings->set_default_str(lib->settings, "%s.journal.default", "1",
|
|
lib->ns);
|
|
|
|
charon->load_loggers(charon);
|
|
|
|
DBG1(DBG_DMN, "Starting charon-systemd IKE daemon (strongSwan "VERSION", "
|
|
"%s %s, %s)", utsname.sysname, utsname.release, utsname.machine);
|
|
|
|
if (!charon->initialize(charon,
|
|
lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns)))
|
|
{
|
|
sd_notifyf(0, "STATUS=charon initialization failed");
|
|
goto error;
|
|
}
|
|
lib->plugins->status(lib->plugins, LEVEL_CTRL);
|
|
|
|
if (!lib->caps->drop(lib->caps))
|
|
{
|
|
sd_notifyf(0, "STATUS=dropping capabilities failed");
|
|
goto error;
|
|
}
|
|
|
|
/* add handler for fatal signals,
|
|
* INT, TERM and HUP are handled by sigwaitinfo() in run() */
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
sigaddset(&action.sa_mask, SIGINT);
|
|
sigaddset(&action.sa_mask, SIGTERM);
|
|
sigaddset(&action.sa_mask, SIGHUP);
|
|
|
|
/* optionally let the external system handle fatal signals */
|
|
#ifndef DISABLE_SIGNAL_HANDLER
|
|
action.sa_handler = segv_handler;
|
|
sigaction(SIGSEGV, &action, NULL);
|
|
sigaction(SIGILL, &action, NULL);
|
|
sigaction(SIGBUS, &action, NULL);
|
|
#endif /* DISABLE_SIGNAL_HANDLER */
|
|
|
|
action.sa_handler = SIG_IGN;
|
|
sigaction(SIGPIPE, &action, NULL);
|
|
|
|
pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
|
|
|
|
charon->start(charon);
|
|
|
|
sd_notifyf(0, "STATUS=charon-systemd running, strongSwan %s, %s %s, %s",
|
|
VERSION, utsname.sysname, utsname.release, utsname.machine);
|
|
|
|
status = run();
|
|
|
|
error:
|
|
libcharon_deinit();
|
|
return status;
|
|
}
|