/* * Copyright (C) 2006-2017 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter * HSR 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 . * * 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. */ /* * Copyright (C) 2016 secunet Security Networks AG * Copyright (C) 2016 Thomas Egerer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #ifdef HAVE_SYSLOG #include #endif #include "daemon.h" #include #include #include #include #include #include #include #include #ifndef LOG_AUTHPRIV /* not defined on OpenSolaris */ #define LOG_AUTHPRIV LOG_AUTH #endif typedef struct private_daemon_t private_daemon_t; /** * Private additions to daemon_t, contains threads and internal functions. */ struct private_daemon_t { /** * Public members of daemon_t. */ daemon_t public; /** * Handler for kernel events */ kernel_handler_t *kernel_handler; /** * A list of installed loggers (as logger_entry_t*) */ linked_list_t *loggers; /** * Cached log levels for default loggers */ level_t *levels; /** * Whether to log to stdout/err by default */ bool to_stderr; /** * Identifier used for syslog (in the openlog call) */ char *syslog_identifier; /** * Mutex for configured loggers */ mutex_t *mutex; /** * Integrity check failed? */ bool integrity_failed; /** * Number of times we have been initialized */ refcount_t ref; }; /** * Register plugins if built statically */ #ifdef STATIC_PLUGIN_CONSTRUCTORS #include "plugin_constructors.c" #endif /** * One and only instance of the daemon. */ daemon_t *charon; /** * hook in library for debugging messages */ extern void (*dbg) (debug_t group, level_t level, char *fmt, ...); /** * we store the previous debug function so we can reset it */ static void (*dbg_old) (debug_t group, level_t level, char *fmt, ...); /** * Logging hook for library logs, spreads debug message over bus */ static void dbg_bus(debug_t group, level_t level, char *fmt, ...) { va_list args; va_start(args, fmt); charon->bus->vlog(charon->bus, group, level, fmt, args); va_end(args); } /** * Data for registered custom loggers */ typedef struct { /** * Name of the custom logger (also used for loglevel configuration) */ char *name; /** * Constructor to be called for custom logger creation */ custom_logger_constructor_t constructor; } custom_logger_entry_t; #define MAX_CUSTOM_LOGGERS 10 /** * Static array for logger registration using __attribute__((constructor)) */ static custom_logger_entry_t custom_loggers[MAX_CUSTOM_LOGGERS]; static int custom_logger_count; /** * Described in header */ void register_custom_logger(char *name, custom_logger_constructor_t constructor) { if (custom_logger_count < MAX_CUSTOM_LOGGERS - 1) { custom_loggers[custom_logger_count].name = name; custom_loggers[custom_logger_count].constructor = constructor; custom_logger_count++; } else { fprintf(stderr, "failed to register custom logger, please increase " "MAX_CUSTOM_LOGGERS"); } } /** * Types of supported loggers */ typedef enum { /** * Syslog logger instance */ SYS_LOGGER, /** * File logger instance */ FILE_LOGGER, /** * Custom logger instance */ CUSTOM_LOGGER, } logger_type_t; /** * Some metadata about configured loggers */ typedef struct { /** * Target of the logger (syslog facility or filename) */ char *target; /** * Type of logger */ logger_type_t type; /** * The actual logger */ union { sys_logger_t *sys; file_logger_t *file; custom_logger_t *custom; } logger; } logger_entry_t; /** * Destroy a logger entry */ static void logger_entry_destroy(logger_entry_t *this) { switch (this->type) { case FILE_LOGGER: DESTROY_IF(this->logger.file); break; case SYS_LOGGER: DESTROY_IF(this->logger.sys); break; case CUSTOM_LOGGER: DESTROY_IF(this->logger.custom); break; } free(this->target); free(this); } /** * Unregister and destroy a logger entry */ static void logger_entry_unregister_destroy(logger_entry_t *this) { switch (this->type) { case FILE_LOGGER: charon->bus->remove_logger(charon->bus, &this->logger.file->logger); break; case SYS_LOGGER: charon->bus->remove_logger(charon->bus, &this->logger.sys->logger); break; case CUSTOM_LOGGER: charon->bus->remove_logger(charon->bus, &this->logger.custom->logger); break; } logger_entry_destroy(this); } CALLBACK(logger_entry_match, bool, logger_entry_t *this, va_list args) { logger_type_t type; char *target; VA_ARGS_VGET(args, target, type); return this->type == type && streq(this->target, target); } /** * Handle configured syslog identifier * * mutex must be locked when calling this function */ static void handle_syslog_identifier(private_daemon_t *this) { #ifdef HAVE_SYSLOG char *identifier; identifier = lib->settings->get_str(lib->settings, "%s.syslog.identifier", NULL, lib->ns); if (identifier) { /* set identifier, which is prepended to each log line */ if (!this->syslog_identifier || !streq(identifier, this->syslog_identifier)) { closelog(); this->syslog_identifier = identifier; openlog(this->syslog_identifier, 0, 0); } } else if (this->syslog_identifier) { closelog(); this->syslog_identifier = NULL; } #endif /* HAVE_SYSLOG */ } /** * Convert the given string into a syslog facility, returns -1 if the facility * is not supported */ static int get_syslog_facility(char *facility) { #ifdef HAVE_SYSLOG if (streq(facility, "daemon")) { return LOG_DAEMON; } else if (streq(facility, "auth")) { return LOG_AUTHPRIV; } #endif /* HAVE_SYSLOG */ return -1; } /** * Returns an existing or newly created logger entry (if found, it is removed * from the given linked list of existing loggers) */ static logger_entry_t *get_logger_entry(char *target, logger_type_t type, linked_list_t *existing, custom_logger_constructor_t constructor) { logger_entry_t *entry; if (!existing->find_first(existing, logger_entry_match, (void**)&entry, target, type)) { INIT(entry, .target = strdup(target), .type = type, ); switch (type) { case FILE_LOGGER: entry->logger.file = file_logger_create(target); break; case SYS_LOGGER: #ifdef HAVE_SYSLOG entry->logger.sys = sys_logger_create( get_syslog_facility(target)); break; #else free(entry); return NULL; #endif /* HAVE_SYSLOG */ case CUSTOM_LOGGER: if (constructor) { entry->logger.custom = constructor(target); } if (!entry->logger.custom) { free(entry); return NULL; } break; } } else { existing->remove(existing, entry, NULL); } return entry; } /** * Create or reuse a syslog logger */ static sys_logger_t *add_sys_logger(private_daemon_t *this, char *facility, linked_list_t *current_loggers) { logger_entry_t *entry; entry = get_logger_entry(facility, SYS_LOGGER, current_loggers, NULL); if (entry) { this->loggers->insert_last(this->loggers, entry); } return entry ? entry->logger.sys : NULL; } /** * Create or reuse a file logger */ static file_logger_t *add_file_logger(private_daemon_t *this, char *filename, linked_list_t *current_loggers) { logger_entry_t *entry; entry = get_logger_entry(filename, FILE_LOGGER, current_loggers, NULL); if (entry) { this->loggers->insert_last(this->loggers, entry); } return entry ? entry->logger.file : NULL; } /** * Create or reuse a custom logger */ static custom_logger_t *add_custom_logger(private_daemon_t *this, custom_logger_entry_t *custom, linked_list_t *current_loggers) { logger_entry_t *entry; entry = get_logger_entry(custom->name, CUSTOM_LOGGER, current_loggers, custom->constructor); if (entry) { this->loggers->insert_last(this->loggers, entry); } return entry ? entry->logger.custom : NULL; } /** * Load the given syslog logger configured in strongswan.conf */ static void load_sys_logger(private_daemon_t *this, char *facility, linked_list_t *current_loggers) { sys_logger_t *sys_logger; debug_t group; level_t def; bool ike_name, log_level; if (get_syslog_facility(facility) == -1) { return; } sys_logger = add_sys_logger(this, facility, current_loggers); if (!sys_logger) { return; } ike_name = lib->settings->get_bool(lib->settings, "%s.syslog.%s.ike_name", FALSE, lib->ns, facility); log_level = lib->settings->get_bool(lib->settings, "%s.syslog.%s.log_level", FALSE, lib->ns, facility); sys_logger->set_options(sys_logger, ike_name, log_level); def = lib->settings->get_int(lib->settings, "%s.syslog.%s.default", 1, lib->ns, facility); for (group = 0; group < DBG_MAX; group++) { sys_logger->set_level(sys_logger, group, lib->settings->get_int(lib->settings, "%s.syslog.%s.%N", def, lib->ns, facility, debug_lower_names, group)); } charon->bus->add_logger(charon->bus, &sys_logger->logger); } /** * Load the given file logger configured in strongswan.conf */ static void load_file_logger(private_daemon_t *this, char *section, linked_list_t *current_loggers) { file_logger_t *file_logger; debug_t group; level_t def; bool add_ms, ike_name, log_level, flush_line, append; char *time_format, *filename; time_format = lib->settings->get_str(lib->settings, "%s.filelog.%s.time_format", NULL, lib->ns, section); add_ms = lib->settings->get_bool(lib->settings, "%s.filelog.%s.time_add_ms", FALSE, lib->ns, section); ike_name = lib->settings->get_bool(lib->settings, "%s.filelog.%s.ike_name", FALSE, lib->ns, section); log_level = lib->settings->get_bool(lib->settings, "%s.filelog.%s.log_level", FALSE, lib->ns, section); flush_line = lib->settings->get_bool(lib->settings, "%s.filelog.%s.flush_line", FALSE, lib->ns, section); append = lib->settings->get_bool(lib->settings, "%s.filelog.%s.append", TRUE, lib->ns, section); filename = lib->settings->get_str(lib->settings, "%s.filelog.%s.path", section, lib->ns, section); file_logger = add_file_logger(this, filename, current_loggers); if (!file_logger) { return; } file_logger->set_options(file_logger, time_format, add_ms, ike_name, log_level); file_logger->open(file_logger, flush_line, append); def = lib->settings->get_int(lib->settings, "%s.filelog.%s.default", 1, lib->ns, section); for (group = 0; group < DBG_MAX; group++) { file_logger->set_level(file_logger, group, lib->settings->get_int(lib->settings, "%s.filelog.%s.%N", def, lib->ns, section, debug_lower_names, group)); } charon->bus->add_logger(charon->bus, &file_logger->logger); } /** * Load the given custom logger configured in strongswan.conf */ static void load_custom_logger(private_daemon_t *this, custom_logger_entry_t *entry, linked_list_t *current_loggers) { custom_logger_t *custom_logger; debug_t group; level_t def; custom_logger = add_custom_logger(this, entry, current_loggers); if (!custom_logger) { return; } def = lib->settings->get_int(lib->settings, "%s.customlog.%s.default", 1, lib->ns, entry->name); for (group = 0; group < DBG_MAX; group++) { custom_logger->set_level(custom_logger, group, lib->settings->get_int(lib->settings, "%s.customlog.%s.%N", def, lib->ns, entry->name, debug_lower_names, group)); } if (custom_logger->reload) { custom_logger->reload(custom_logger); } charon->bus->add_logger(charon->bus, &custom_logger->logger); } METHOD(daemon_t, load_loggers, void, private_daemon_t *this) { enumerator_t *enumerator; linked_list_t *current_loggers; char *target; int i; this->mutex->lock(this->mutex); handle_syslog_identifier(this); current_loggers = this->loggers; this->loggers = linked_list_create(); enumerator = lib->settings->create_section_enumerator(lib->settings, "%s.syslog", lib->ns); while (enumerator->enumerate(enumerator, &target)) { load_sys_logger(this, target, current_loggers); } enumerator->destroy(enumerator); enumerator = lib->settings->create_section_enumerator(lib->settings, "%s.filelog", lib->ns); while (enumerator->enumerate(enumerator, &target)) { load_file_logger(this, target, current_loggers); } enumerator->destroy(enumerator); for (i = 0; i < custom_logger_count; ++i) { load_custom_logger(this, &custom_loggers[i], current_loggers); } if (!this->loggers->get_count(this->loggers) && this->levels) { /* setup legacy style default loggers configured via command-line */ file_logger_t *file_logger; sys_logger_t *sys_logger; debug_t group; sys_logger = add_sys_logger(this, "daemon", current_loggers); file_logger = add_file_logger(this, "stdout", current_loggers); file_logger->open(file_logger, FALSE, FALSE); for (group = 0; group < DBG_MAX; group++) { if (sys_logger) { sys_logger->set_level(sys_logger, group, this->levels[group]); } if (this->to_stderr) { file_logger->set_level(file_logger, group, this->levels[group]); } } if (sys_logger) { charon->bus->add_logger(charon->bus, &sys_logger->logger); } charon->bus->add_logger(charon->bus, &file_logger->logger); sys_logger = add_sys_logger(this, "auth", current_loggers); if (sys_logger) { sys_logger->set_level(sys_logger, DBG_ANY, LEVEL_AUDIT); charon->bus->add_logger(charon->bus, &sys_logger->logger); } } /* unregister and destroy any unused remaining loggers */ current_loggers->destroy_function(current_loggers, (void*)logger_entry_unregister_destroy); this->mutex->unlock(this->mutex); } METHOD(daemon_t, set_default_loggers, void, private_daemon_t *this, level_t levels[DBG_MAX], bool to_stderr) { debug_t group; this->mutex->lock(this->mutex); if (!levels) { free(this->levels); this->levels = NULL; } else { if (!this->levels) { this->levels = calloc(sizeof(level_t), DBG_MAX); } for (group = 0; group < DBG_MAX; group++) { this->levels[group] = levels[group]; } this->to_stderr = to_stderr; } this->mutex->unlock(this->mutex); } METHOD(daemon_t, set_level, void, private_daemon_t *this, debug_t group, level_t level) { enumerator_t *enumerator; logger_entry_t *entry; /* we set the loglevel on ALL loggers */ this->mutex->lock(this->mutex); enumerator = this->loggers->create_enumerator(this->loggers); while (enumerator->enumerate(enumerator, &entry)) { switch (entry->type) { case FILE_LOGGER: entry->logger.file->set_level(entry->logger.file, group, level); charon->bus->add_logger(charon->bus, &entry->logger.file->logger); break; case SYS_LOGGER: entry->logger.sys->set_level(entry->logger.sys, group, level); charon->bus->add_logger(charon->bus, &entry->logger.sys->logger); break; case CUSTOM_LOGGER: entry->logger.custom->set_level(entry->logger.custom, group, level); charon->bus->add_logger(charon->bus, &entry->logger.custom->logger); break; } } enumerator->destroy(enumerator); this->mutex->unlock(this->mutex); } /** * Clean up all daemon resources */ static void destroy(private_daemon_t *this) { /* terminate all idle threads */ lib->processor->set_threads(lib->processor, 0); /* make sure nobody waits for a DNS query */ lib->hosts->flush(lib->hosts); /* close all IKE_SAs */ if (this->public.ike_sa_manager) { this->public.ike_sa_manager->flush(this->public.ike_sa_manager); } if (this->public.traps) { this->public.traps->flush(this->public.traps); } if (this->public.shunts) { this->public.shunts->flush(this->public.shunts); } if (this->public.sender) { this->public.sender->flush(this->public.sender); } /* cancel all threads and wait for their termination */ lib->processor->cancel(lib->processor); #ifdef ME DESTROY_IF(this->public.connect_manager); DESTROY_IF(this->public.mediation_manager); #endif /* ME */ /* make sure the cache and scheduler are clear before unloading plugins */ lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); lib->scheduler->flush(lib->scheduler); lib->plugins->unload(lib->plugins); DESTROY_IF(this->public.attributes); DESTROY_IF(this->kernel_handler); DESTROY_IF(this->public.traps); DESTROY_IF(this->public.shunts); DESTROY_IF(this->public.redirect); DESTROY_IF(this->public.controller); DESTROY_IF(this->public.eap); DESTROY_IF(this->public.xauth); DESTROY_IF(this->public.backends); DESTROY_IF(this->public.socket); DESTROY_IF(this->public.kernel); /* rehook library logging, shutdown logging */ dbg = dbg_old; DESTROY_IF(this->public.bus); this->loggers->destroy_function(this->loggers, (void*)logger_entry_destroy); this->mutex->destroy(this->mutex); free(this->levels); free(this); } /** * Run a set of configured scripts */ static void run_scripts(private_daemon_t *this, char *verb) { struct { char *name; char *path; } *script; array_t *scripts = NULL; enumerator_t *enumerator; char *key, *value, *pos, buf[1024]; FILE *cmd; /* copy the scripts so we don't hold any locks while executing them */ enumerator = lib->settings->create_key_value_enumerator(lib->settings, "%s.%s-scripts", lib->ns, verb); while (enumerator->enumerate(enumerator, &key, &value)) { INIT(script, .name = key, .path = value, ); array_insert_create(&scripts, ARRAY_TAIL, script); } enumerator->destroy(enumerator); enumerator = array_create_enumerator(scripts); while (enumerator->enumerate(enumerator, &script)) { DBG1(DBG_DMN, "executing %s script '%s' (%s)", verb, script->name, script->path); cmd = popen(script->path, "r"); if (!cmd) { DBG1(DBG_DMN, "executing %s script '%s' (%s) failed: %s", verb, script->name, script->path, strerror(errno)); } else { while (TRUE) { if (!fgets(buf, sizeof(buf), cmd)) { if (ferror(cmd)) { DBG1(DBG_DMN, "reading from %s script '%s' (%s) failed", verb, script->name, script->path); } break; } else { pos = buf + strlen(buf); if (pos > buf && pos[-1] == '\n') { pos[-1] = '\0'; } DBG1(DBG_DMN, "%s: %s", script->name, buf); } } pclose(cmd); } free(script); } enumerator->destroy(enumerator); array_destroy(scripts); } METHOD(daemon_t, start, void, private_daemon_t *this) { /* start the engine, go multithreaded */ lib->processor->set_threads(lib->processor, lib->settings->get_int(lib->settings, "%s.threads", DEFAULT_THREADS, lib->ns)); run_scripts(this, "start"); } /** * Initialize/deinitialize sender and receiver */ static bool sender_receiver_cb(void *plugin, plugin_feature_t *feature, bool reg, private_daemon_t *this) { if (reg) { this->public.receiver = receiver_create(); if (!this->public.receiver) { return FALSE; } this->public.sender = sender_create(); } else { DESTROY_IF(this->public.receiver); DESTROY_IF(this->public.sender); } return TRUE; } /** * Initialize/deinitialize IKE_SA/CHILD_SA managers */ static bool sa_managers_cb(void *plugin, plugin_feature_t *feature, bool reg, private_daemon_t *this) { if (reg) { this->public.ike_sa_manager = ike_sa_manager_create(); if (!this->public.ike_sa_manager) { return FALSE; } this->public.child_sa_manager = child_sa_manager_create(); } else { DESTROY_IF(this->public.ike_sa_manager); DESTROY_IF(this->public.child_sa_manager); } return TRUE; } METHOD(daemon_t, initialize, bool, private_daemon_t *this, char *plugins) { plugin_feature_t features[] = { PLUGIN_PROVIDE(CUSTOM, "libcharon"), PLUGIN_DEPENDS(NONCE_GEN), PLUGIN_DEPENDS(CUSTOM, "libcharon-sa-managers"), PLUGIN_DEPENDS(CUSTOM, "libcharon-receiver"), PLUGIN_DEPENDS(CUSTOM, "kernel-ipsec"), PLUGIN_DEPENDS(CUSTOM, "kernel-net"), PLUGIN_CALLBACK((plugin_feature_callback_t)sender_receiver_cb, this), PLUGIN_PROVIDE(CUSTOM, "libcharon-receiver"), PLUGIN_DEPENDS(HASHER, HASH_SHA1), PLUGIN_DEPENDS(RNG, RNG_STRONG), PLUGIN_DEPENDS(CUSTOM, "socket"), PLUGIN_CALLBACK((plugin_feature_callback_t)sa_managers_cb, this), PLUGIN_PROVIDE(CUSTOM, "libcharon-sa-managers"), PLUGIN_DEPENDS(HASHER, HASH_SHA1), PLUGIN_DEPENDS(RNG, RNG_WEAK), }; lib->plugins->add_static_features(lib->plugins, lib->ns, features, countof(features), TRUE, NULL, NULL); /* load plugins, further infrastructure may need it */ if (!lib->plugins->load(lib->plugins, plugins)) { return FALSE; } /* Queue start_action job */ lib->processor->queue_job(lib->processor, (job_t*)start_action_job_create()); #ifdef ME this->public.connect_manager = connect_manager_create(); if (this->public.connect_manager == NULL) { return FALSE; } this->public.mediation_manager = mediation_manager_create(); #endif /* ME */ return TRUE; } /** * Create the daemon. */ private_daemon_t *daemon_create() { private_daemon_t *this; INIT(this, .public = { .initialize = _initialize, .start = _start, .load_loggers = _load_loggers, .set_default_loggers = _set_default_loggers, .set_level = _set_level, .bus = bus_create(), }, .loggers = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .ref = 1, ); charon = &this->public; this->public.kernel = kernel_interface_create(); this->public.attributes = attribute_manager_create(); this->public.controller = controller_create(); this->public.eap = eap_manager_create(); this->public.xauth = xauth_manager_create(); this->public.backends = backend_manager_create(); this->public.socket = socket_manager_create(); this->public.traps = trap_manager_create(); this->public.shunts = shunt_manager_create(); this->public.redirect = redirect_manager_create(); this->kernel_handler = kernel_handler_create(); return this; } /** * Described in header. */ void libcharon_deinit() { private_daemon_t *this = (private_daemon_t*)charon; if (!this || !ref_put(&this->ref)) { /* have more users */ return; } run_scripts(this, "stop"); destroy(this); charon = NULL; } /** * Described in header. */ bool libcharon_init() { private_daemon_t *this; if (charon) { /* already initialized, increase refcount */ this = (private_daemon_t*)charon; ref_get(&this->ref); return !this->integrity_failed; } this = daemon_create(); /* for uncritical pseudo random numbers */ srandom(time(NULL) + getpid()); /* set up hook to log dbg message in library via charons message bus */ dbg_old = dbg; dbg = dbg_bus; if (lib->integrity && !lib->integrity->check(lib->integrity, "libcharon", libcharon_init)) { dbg(DBG_DMN, 1, "integrity check of libcharon failed"); this->integrity_failed = TRUE; } return !this->integrity_failed; }