1178 lines
28 KiB
C
1178 lines
28 KiB
C
/*
|
|
* Copyright (C) 2011-2020 Tobias Brunner
|
|
* Copyright (C) 2006 Martin Willi
|
|
* 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 <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 "bus.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <threading/thread.h>
|
|
#include <threading/thread_value.h>
|
|
#include <threading/mutex.h>
|
|
#include <threading/rwlock.h>
|
|
|
|
/**
|
|
* These operations allow us to speed up the log level checks on some platforms.
|
|
* In particular if acquiring the read lock is expensive even in the absence of
|
|
* any writers.
|
|
*
|
|
* Note that while holding the read/write lock the read does not have to be
|
|
* atomic as the write lock must be held to set the level.
|
|
*/
|
|
#ifdef HAVE_GCC_ATOMIC_OPERATIONS
|
|
|
|
#define skip_level(ptr, level) (__atomic_load_n(ptr, __ATOMIC_RELAXED) < level)
|
|
#define set_level(ptr, val) __atomic_store_n(ptr, val, __ATOMIC_RELAXED)
|
|
|
|
#elif defined(HAVE_GCC_SYNC_OPERATIONS)
|
|
|
|
#define skip_level(ptr, level) (__sync_fetch_and_add(ptr, 0) < level)
|
|
#define set_level(ptr, val) __sync_bool_compare_and_swap(ptr, *ptr, val)
|
|
|
|
#else
|
|
|
|
#define skip_level(ptr, level) FALSE
|
|
#define set_level(ptr, val) ({ *ptr = val; })
|
|
|
|
#endif
|
|
|
|
typedef struct private_bus_t private_bus_t;
|
|
|
|
/**
|
|
* Private data of a bus_t object.
|
|
*/
|
|
struct private_bus_t {
|
|
/**
|
|
* Public part of a bus_t object.
|
|
*/
|
|
bus_t public;
|
|
|
|
/**
|
|
* List of registered listeners as entry_t.
|
|
*/
|
|
linked_list_t *listeners;
|
|
|
|
/**
|
|
* List of registered loggers for each log group as log_entry_t.
|
|
* Loggers are ordered by descending log level.
|
|
* The extra list stores all loggers so we can properly unregister them.
|
|
*/
|
|
linked_list_t *loggers[DBG_MAX + 1];
|
|
|
|
/**
|
|
* Maximum log level of any registered logger for each log group.
|
|
* This allows to check quickly if a log message has to be logged at all.
|
|
*/
|
|
level_t max_level[DBG_MAX + 1];
|
|
|
|
/**
|
|
* Same as max level, but for loggers using the vlog() method.
|
|
*/
|
|
level_t max_vlevel[DBG_MAX + 1];
|
|
|
|
/**
|
|
* Mutex for the list of listeners, recursively.
|
|
*/
|
|
mutex_t *mutex;
|
|
|
|
/**
|
|
* Read-write lock for the list of loggers.
|
|
*/
|
|
rwlock_t *log_lock;
|
|
|
|
/**
|
|
* Thread local storage the threads IKE_SA
|
|
*/
|
|
thread_value_t *thread_sa;
|
|
};
|
|
|
|
typedef struct entry_t entry_t;
|
|
|
|
/**
|
|
* a listener entry
|
|
*/
|
|
struct entry_t {
|
|
|
|
/**
|
|
* registered listener interface
|
|
*/
|
|
listener_t *listener;
|
|
|
|
/**
|
|
* are we currently calling this listener
|
|
*/
|
|
int calling;
|
|
|
|
};
|
|
|
|
typedef struct log_entry_t log_entry_t;
|
|
|
|
/**
|
|
* a logger entry
|
|
*/
|
|
struct log_entry_t {
|
|
|
|
/**
|
|
* registered logger interface
|
|
*/
|
|
logger_t *logger;
|
|
|
|
/**
|
|
* registered log levels per group
|
|
*/
|
|
level_t levels[DBG_MAX];
|
|
|
|
};
|
|
|
|
METHOD(bus_t, add_listener, void,
|
|
private_bus_t *this, listener_t *listener)
|
|
{
|
|
entry_t *entry;
|
|
|
|
INIT(entry,
|
|
.listener = listener,
|
|
);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
this->listeners->insert_last(this->listeners, entry);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, remove_listener, void,
|
|
private_bus_t *this, listener_t *listener)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->listener == listener)
|
|
{
|
|
this->listeners->remove_at(this->listeners, enumerator);
|
|
free(entry);
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
/**
|
|
* Register a logger on the given log group according to the requested level
|
|
*/
|
|
static inline void register_logger(private_bus_t *this, debug_t group,
|
|
log_entry_t *entry)
|
|
{
|
|
enumerator_t *enumerator;
|
|
linked_list_t *loggers;
|
|
log_entry_t *current;
|
|
level_t level;
|
|
|
|
loggers = this->loggers[group];
|
|
level = entry->levels[group];
|
|
|
|
enumerator = loggers->create_enumerator(loggers);
|
|
while (enumerator->enumerate(enumerator, (void**)¤t))
|
|
{
|
|
if (current->levels[group] <= level)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
loggers->insert_before(loggers, enumerator, entry);
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (entry->logger->log)
|
|
{
|
|
set_level(&this->max_level[group], max(this->max_level[group], level));
|
|
}
|
|
if (entry->logger->vlog)
|
|
{
|
|
set_level(&this->max_vlevel[group],
|
|
max(this->max_vlevel[group], level));
|
|
}
|
|
}
|
|
|
|
CALLBACK(find_max_levels, bool,
|
|
log_entry_t *entry, va_list args)
|
|
{
|
|
level_t *level, *vlevel;
|
|
debug_t group;
|
|
|
|
VA_ARGS_VGET(args, group, level, vlevel);
|
|
if (entry->logger->log && *level == LEVEL_SILENT)
|
|
{
|
|
*level = entry->levels[group];
|
|
}
|
|
if (entry->logger->vlog && *vlevel == LEVEL_SILENT)
|
|
{
|
|
*vlevel = entry->levels[group];
|
|
}
|
|
return *level > LEVEL_SILENT && *vlevel > LEVEL_SILENT;
|
|
}
|
|
|
|
/**
|
|
* Unregister a logger from all log groups (destroys the log_entry_t)
|
|
*/
|
|
static inline void unregister_logger(private_bus_t *this, logger_t *logger)
|
|
{
|
|
enumerator_t *enumerator;
|
|
linked_list_t *loggers;
|
|
log_entry_t *entry, *found = NULL;
|
|
debug_t group;
|
|
|
|
loggers = this->loggers[DBG_MAX];
|
|
enumerator = loggers->create_enumerator(loggers);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->logger == logger)
|
|
{
|
|
loggers->remove_at(loggers, enumerator);
|
|
found = entry;
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (found)
|
|
{
|
|
for (group = 0; group < DBG_MAX; group++)
|
|
{
|
|
if (found->levels[group] > LEVEL_SILENT)
|
|
{
|
|
level_t level = LEVEL_SILENT, vlevel = LEVEL_SILENT;
|
|
|
|
loggers = this->loggers[group];
|
|
loggers->remove(loggers, found, NULL);
|
|
loggers->find_first(loggers, find_max_levels, NULL, group,
|
|
&level, &vlevel);
|
|
set_level(&this->max_level[group], level);
|
|
set_level(&this->max_vlevel[group], vlevel);
|
|
}
|
|
}
|
|
free(found);
|
|
}
|
|
}
|
|
|
|
METHOD(bus_t, add_logger, void,
|
|
private_bus_t *this, logger_t *logger)
|
|
{
|
|
log_entry_t *entry;
|
|
debug_t group;
|
|
|
|
INIT(entry,
|
|
.logger = logger,
|
|
);
|
|
|
|
this->log_lock->write_lock(this->log_lock);
|
|
unregister_logger(this, logger);
|
|
for (group = 0; group < DBG_MAX; group++)
|
|
{
|
|
entry->levels[group] = logger->get_level(logger, group);
|
|
if (entry->levels[group] > LEVEL_SILENT)
|
|
{
|
|
register_logger(this, group, entry);
|
|
}
|
|
}
|
|
this->loggers[DBG_MAX]->insert_last(this->loggers[DBG_MAX], entry);
|
|
this->log_lock->unlock(this->log_lock);
|
|
}
|
|
|
|
METHOD(bus_t, remove_logger, void,
|
|
private_bus_t *this, logger_t *logger)
|
|
{
|
|
this->log_lock->write_lock(this->log_lock);
|
|
unregister_logger(this, logger);
|
|
this->log_lock->unlock(this->log_lock);
|
|
}
|
|
|
|
METHOD(bus_t, set_sa, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa)
|
|
{
|
|
this->thread_sa->set(this->thread_sa, ike_sa);
|
|
}
|
|
|
|
METHOD(bus_t, get_sa, ike_sa_t*,
|
|
private_bus_t *this)
|
|
{
|
|
return this->thread_sa->get(this->thread_sa);
|
|
}
|
|
|
|
/**
|
|
* data associated to a signal, passed to callback
|
|
*/
|
|
typedef struct {
|
|
/** associated IKE_SA */
|
|
ike_sa_t *ike_sa;
|
|
/** invoking thread */
|
|
long thread;
|
|
/** debug group */
|
|
debug_t group;
|
|
/** debug level */
|
|
level_t level;
|
|
/** message/fmt */
|
|
char *message;
|
|
/** argument list if message is a format string for vlog() */
|
|
va_list args;
|
|
} log_data_t;
|
|
|
|
CALLBACK(log_cb, void,
|
|
log_entry_t *entry, va_list args)
|
|
{
|
|
log_data_t *data;
|
|
|
|
VA_ARGS_VGET(args, data);
|
|
if (entry->logger->log && entry->levels[data->group] >= data->level)
|
|
{
|
|
entry->logger->log(entry->logger, data->group, data->level,
|
|
data->thread, data->ike_sa, data->message);
|
|
}
|
|
}
|
|
|
|
CALLBACK(vlog_cb, void,
|
|
log_entry_t *entry, va_list args)
|
|
{
|
|
log_data_t *data;
|
|
|
|
VA_ARGS_VGET(args, data);
|
|
if (entry->logger->vlog && entry->levels[data->group] >= data->level)
|
|
{
|
|
va_list copy;
|
|
|
|
va_copy(copy, data->args);
|
|
entry->logger->vlog(entry->logger, data->group, data->level,
|
|
data->thread, data->ike_sa, data->message, copy);
|
|
va_end(copy);
|
|
}
|
|
}
|
|
|
|
METHOD(bus_t, vlog, void,
|
|
private_bus_t *this, debug_t group, level_t level,
|
|
char* format, va_list args)
|
|
{
|
|
linked_list_t *loggers;
|
|
log_data_t data;
|
|
|
|
/* NOTE: This is not 100% thread-safe and done here only because it is
|
|
* performance critical. We therefore ignore the following two issues for
|
|
* this particular case: 1) We might miss some log messages if another
|
|
* thread concurrently increases the log level or registers a new logger.
|
|
* 2) We might have to acquire the read lock below even if it wouldn't be
|
|
* necessary anymore due to another thread concurrently unregistering a
|
|
* logger or reducing the level. */
|
|
if (skip_level(&this->max_level[group], level) &&
|
|
skip_level(&this->max_vlevel[group], level))
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->log_lock->read_lock(this->log_lock);
|
|
loggers = this->loggers[group];
|
|
|
|
if (this->max_level[group] >= level)
|
|
{
|
|
char buf[1024];
|
|
ssize_t len;
|
|
|
|
data.ike_sa = this->thread_sa->get(this->thread_sa);
|
|
data.thread = thread_current_id();
|
|
data.group = group;
|
|
data.level = level;
|
|
data.message = buf;
|
|
|
|
va_copy(data.args, args);
|
|
len = vsnprintf(data.message, sizeof(buf), format, data.args);
|
|
va_end(data.args);
|
|
if (len >= sizeof(buf))
|
|
{
|
|
len++;
|
|
data.message = malloc(len);
|
|
va_copy(data.args, args);
|
|
len = vsnprintf(data.message, len, format, data.args);
|
|
va_end(data.args);
|
|
}
|
|
if (len > 0)
|
|
{
|
|
loggers->invoke_function(loggers, log_cb, &data);
|
|
}
|
|
if (data.message != buf)
|
|
{
|
|
free(data.message);
|
|
}
|
|
}
|
|
if (this->max_vlevel[group] >= level)
|
|
{
|
|
data.ike_sa = this->thread_sa->get(this->thread_sa);
|
|
data.thread = thread_current_id();
|
|
data.group = group;
|
|
data.level = level;
|
|
data.message = format;
|
|
|
|
va_copy(data.args, args);
|
|
loggers->invoke_function(loggers, vlog_cb, &data);
|
|
va_end(data.args);
|
|
}
|
|
|
|
this->log_lock->unlock(this->log_lock);
|
|
}
|
|
|
|
METHOD(bus_t, log_, void,
|
|
private_bus_t *this, debug_t group, level_t level, char* format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vlog(this, group, level, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/**
|
|
* unregister a listener
|
|
*/
|
|
static inline void unregister_listener(private_bus_t *this, entry_t *entry,
|
|
enumerator_t *enumerator)
|
|
{
|
|
this->listeners->remove_at(this->listeners, enumerator);
|
|
free(entry);
|
|
}
|
|
|
|
METHOD(bus_t, alert, void,
|
|
private_bus_t *this, alert_t alert, ...)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
va_list args;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->alert)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
va_start(args, alert);
|
|
keep = entry->listener->alert(entry->listener, ike_sa, alert, args);
|
|
va_end(args);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_state_change, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_state_change)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_state_change(entry->listener, ike_sa, state);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, child_state_change, void,
|
|
private_bus_t *this, child_sa_t *child_sa, child_sa_state_t state)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->child_state_change)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->child_state_change(entry->listener, ike_sa,
|
|
child_sa, state);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, message, void,
|
|
private_bus_t *this, message_t *message, bool incoming, bool plain)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->message)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->message(entry->listener, ike_sa,
|
|
message, incoming, plain);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_keys, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
|
|
chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r,
|
|
ike_sa_t *rekey, shared_key_t *shared, auth_method_t method)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_keys)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, dh_other,
|
|
nonce_i, nonce_r, rekey, shared,
|
|
method);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_derived_keys, void,
|
|
private_bus_t *this, chunk_t sk_ei, chunk_t sk_er, chunk_t sk_ai,
|
|
chunk_t sk_ar)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_derived_keys)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_derived_keys(entry->listener, ike_sa, sk_ei,
|
|
sk_er, sk_ai, sk_ar);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, child_keys, void,
|
|
private_bus_t *this, child_sa_t *child_sa, bool initiator,
|
|
diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->child_keys)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->child_keys(entry->listener, ike_sa,
|
|
child_sa, initiator, dh, nonce_i, nonce_r);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, child_derived_keys, void,
|
|
private_bus_t *this, child_sa_t *child_sa, bool initiator,
|
|
chunk_t encr_i, chunk_t encr_r, chunk_t integ_i, chunk_t integ_r)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->child_derived_keys)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->child_derived_keys(entry->listener, ike_sa,
|
|
child_sa, initiator, encr_i, encr_r,
|
|
integ_i, integ_r);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, child_updown, void,
|
|
private_bus_t *this, child_sa_t *child_sa, bool up)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->child_updown)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->child_updown(entry->listener,
|
|
ike_sa, child_sa, up);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, child_rekey, void,
|
|
private_bus_t *this, child_sa_t *old, child_sa_t *new)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->child_rekey)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->child_rekey(entry->listener, ike_sa,
|
|
old, new);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, children_migrate, void,
|
|
private_bus_t *this, ike_sa_id_t *new, uint32_t unique)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->children_migrate)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->children_migrate(entry->listener, ike_sa, new,
|
|
unique);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_updown, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa, bool up)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_updown)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_updown(entry->listener, ike_sa, up);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
|
|
/* a down event for IKE_SA implicitly downs all CHILD_SAs */
|
|
if (!up)
|
|
{
|
|
enumerator_t *enumerator;
|
|
child_sa_t *child_sa;
|
|
|
|
enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
|
|
while (enumerator->enumerate(enumerator, (void**)&child_sa))
|
|
{
|
|
if (child_sa->get_state(child_sa) != CHILD_REKEYED &&
|
|
child_sa->get_state(child_sa) != CHILD_DELETED)
|
|
{
|
|
child_updown(this, child_sa, FALSE);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
}
|
|
}
|
|
|
|
METHOD(bus_t, ike_rekey, void,
|
|
private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_rekey)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_rekey(entry->listener, old, new);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_update, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa, host_t *local, host_t *remote)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_update)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_update(entry->listener, ike_sa, local,
|
|
remote);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_reestablish_pre, void,
|
|
private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_reestablish_pre)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_reestablish_pre(entry->listener, old, new);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, ike_reestablish_post, void,
|
|
private_bus_t *this, ike_sa_t *old, ike_sa_t *new, bool initiated)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->ike_reestablish_post)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->ike_reestablish_post(entry->listener, old, new,
|
|
initiated);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, authorize, bool,
|
|
private_bus_t *this, bool final)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep, success = TRUE;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->authorize)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->authorize(entry->listener, ike_sa,
|
|
final, &success);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
if (!success)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
if (!success)
|
|
{
|
|
alert(this, ALERT_AUTHORIZATION_FAILED);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
METHOD(bus_t, narrow, void,
|
|
private_bus_t *this, child_sa_t *child_sa, narrow_hook_t type,
|
|
linked_list_t *local, linked_list_t *remote)
|
|
{
|
|
enumerator_t *enumerator;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
ike_sa = this->thread_sa->get(this->thread_sa);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->narrow)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->narrow(entry->listener, ike_sa, child_sa,
|
|
type, local, remote);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, assign_vips, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa, bool assign)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->assign_vips)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->assign_vips(entry->listener, ike_sa, assign);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
METHOD(bus_t, handle_vips, void,
|
|
private_bus_t *this, ike_sa_t *ike_sa, bool handle)
|
|
{
|
|
enumerator_t *enumerator;
|
|
entry_t *entry;
|
|
bool keep;
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->listeners->create_enumerator(this->listeners);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->calling || !entry->listener->handle_vips)
|
|
{
|
|
continue;
|
|
}
|
|
entry->calling++;
|
|
keep = entry->listener->handle_vips(entry->listener, ike_sa, handle);
|
|
entry->calling--;
|
|
if (!keep)
|
|
{
|
|
unregister_listener(this, entry, enumerator);
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
}
|
|
|
|
/**
|
|
* Credential manager hook function to forward bus alerts
|
|
*/
|
|
static void hook_creds(private_bus_t *this, credential_hook_type_t type,
|
|
certificate_t *cert)
|
|
{
|
|
switch (type)
|
|
{
|
|
case CRED_HOOK_EXPIRED:
|
|
return alert(this, ALERT_CERT_EXPIRED, cert);
|
|
case CRED_HOOK_REVOKED:
|
|
return alert(this, ALERT_CERT_REVOKED, cert);
|
|
case CRED_HOOK_VALIDATION_FAILED:
|
|
return alert(this, ALERT_CERT_VALIDATION_FAILED, cert);
|
|
case CRED_HOOK_NO_ISSUER:
|
|
return alert(this, ALERT_CERT_NO_ISSUER, cert);
|
|
case CRED_HOOK_UNTRUSTED_ROOT:
|
|
return alert(this, ALERT_CERT_UNTRUSTED_ROOT, cert);
|
|
case CRED_HOOK_EXCEEDED_PATH_LEN:
|
|
return alert(this, ALERT_CERT_EXCEEDED_PATH_LEN, cert);
|
|
case CRED_HOOK_POLICY_VIOLATION:
|
|
return alert(this, ALERT_CERT_POLICY_VIOLATION, cert);
|
|
}
|
|
}
|
|
|
|
METHOD(bus_t, destroy, void,
|
|
private_bus_t *this)
|
|
{
|
|
debug_t group;
|
|
|
|
lib->credmgr->set_hook(lib->credmgr, NULL, NULL);
|
|
for (group = 0; group < DBG_MAX; group++)
|
|
{
|
|
this->loggers[group]->destroy(this->loggers[group]);
|
|
}
|
|
this->loggers[DBG_MAX]->destroy_function(this->loggers[DBG_MAX],
|
|
(void*)free);
|
|
this->listeners->destroy_function(this->listeners, (void*)free);
|
|
this->thread_sa->destroy(this->thread_sa);
|
|
this->log_lock->destroy(this->log_lock);
|
|
this->mutex->destroy(this->mutex);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
bus_t *bus_create()
|
|
{
|
|
private_bus_t *this;
|
|
debug_t group;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.add_listener = _add_listener,
|
|
.remove_listener = _remove_listener,
|
|
.add_logger = _add_logger,
|
|
.remove_logger = _remove_logger,
|
|
.set_sa = _set_sa,
|
|
.get_sa = _get_sa,
|
|
.log = _log_,
|
|
.vlog = _vlog,
|
|
.alert = _alert,
|
|
.ike_state_change = _ike_state_change,
|
|
.child_state_change = _child_state_change,
|
|
.message = _message,
|
|
.ike_keys = _ike_keys,
|
|
.ike_derived_keys = _ike_derived_keys,
|
|
.child_keys = _child_keys,
|
|
.child_derived_keys = _child_derived_keys,
|
|
.ike_updown = _ike_updown,
|
|
.ike_rekey = _ike_rekey,
|
|
.ike_update = _ike_update,
|
|
.ike_reestablish_pre = _ike_reestablish_pre,
|
|
.ike_reestablish_post = _ike_reestablish_post,
|
|
.child_updown = _child_updown,
|
|
.child_rekey = _child_rekey,
|
|
.children_migrate = _children_migrate,
|
|
.authorize = _authorize,
|
|
.narrow = _narrow,
|
|
.assign_vips = _assign_vips,
|
|
.handle_vips = _handle_vips,
|
|
.destroy = _destroy,
|
|
},
|
|
.listeners = linked_list_create(),
|
|
.mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
|
|
.log_lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
|
.thread_sa = thread_value_create(NULL),
|
|
);
|
|
|
|
for (group = 0; group <= DBG_MAX; group++)
|
|
{
|
|
this->loggers[group] = linked_list_create();
|
|
this->max_level[group] = LEVEL_SILENT;
|
|
this->max_vlevel[group] = LEVEL_SILENT;
|
|
}
|
|
|
|
lib->credmgr->set_hook(lib->credmgr, (credential_hook_t)hook_creds, this);
|
|
|
|
return &this->public;
|
|
}
|