427 lines
9.2 KiB
C
427 lines
9.2 KiB
C
/*
|
|
* Copyright (C) 2012 Martin Willi
|
|
* Copyright (C) 2012 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 "unity_handler.h"
|
|
|
|
#include <daemon.h>
|
|
#include <threading/mutex.h>
|
|
#include <utils/linked_list.h>
|
|
#include <processing/jobs/callback_job.h>
|
|
|
|
typedef struct private_unity_handler_t private_unity_handler_t;
|
|
|
|
/**
|
|
* Private data of an unity_handler_t object.
|
|
*/
|
|
struct private_unity_handler_t {
|
|
|
|
/**
|
|
* Public unity_handler_t interface.
|
|
*/
|
|
unity_handler_t public;
|
|
|
|
/**
|
|
* List of subnets to include, as entry_t
|
|
*/
|
|
linked_list_t *include;
|
|
|
|
/**
|
|
* Mutex for concurrent access to lists
|
|
*/
|
|
mutex_t *mutex;
|
|
};
|
|
|
|
/**
|
|
* Traffic selector entry for networks to include under a given IKE_SA
|
|
*/
|
|
typedef struct {
|
|
/** associated IKE_SA, unique ID */
|
|
u_int32_t sa;
|
|
/** traffic selector to include/exclude */
|
|
traffic_selector_t *ts;
|
|
} entry_t;
|
|
|
|
/**
|
|
* Clean up an entry
|
|
*/
|
|
static void entry_destroy(entry_t *this)
|
|
{
|
|
this->ts->destroy(this->ts);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* Create a traffic selector from a unity subnet definition
|
|
*/
|
|
static traffic_selector_t *create_ts(chunk_t subnet)
|
|
{
|
|
chunk_t net, mask;
|
|
int i;
|
|
|
|
if (subnet.len != 8)
|
|
{
|
|
return NULL;
|
|
}
|
|
net = chunk_create(subnet.ptr, 4);
|
|
mask = chunk_clonea(chunk_skip(subnet, 4));
|
|
for (i = 0; i < net.len; i++)
|
|
{
|
|
mask.ptr[i] = (mask.ptr[i] ^ 0xFF) | net.ptr[i];
|
|
}
|
|
return traffic_selector_create_from_bytes(0, TS_IPV4_ADDR_RANGE,
|
|
net, 0, mask, 65535);
|
|
}
|
|
|
|
/**
|
|
* Store a subnet to include in tunnels under this IKE_SA
|
|
*/
|
|
static bool add_include(private_unity_handler_t *this, chunk_t subnet)
|
|
{
|
|
traffic_selector_t *ts;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
|
|
ike_sa = charon->bus->get_sa(charon->bus);
|
|
if (!ike_sa)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ts = create_ts(subnet);
|
|
if (!ts)
|
|
{
|
|
return FALSE;
|
|
}
|
|
INIT(entry,
|
|
.sa = ike_sa->get_unique_id(ike_sa),
|
|
.ts = ts,
|
|
);
|
|
|
|
this->mutex->lock(this->mutex);
|
|
this->include->insert_last(this->include, entry);
|
|
this->mutex->unlock(this->mutex);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Rempve a subnet from the inclusion list for this IKE_SA
|
|
*/
|
|
static bool remove_include(private_unity_handler_t *this, chunk_t subnet)
|
|
{
|
|
enumerator_t *enumerator;
|
|
traffic_selector_t *ts;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
|
|
ike_sa = charon->bus->get_sa(charon->bus);
|
|
if (!ike_sa)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ts = create_ts(subnet);
|
|
if (!ts)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
this->mutex->lock(this->mutex);
|
|
enumerator = this->include->create_enumerator(this->include);
|
|
while (enumerator->enumerate(enumerator, &entry))
|
|
{
|
|
if (entry->sa == ike_sa->get_unique_id(ike_sa) &&
|
|
ts->equals(ts, entry->ts))
|
|
{
|
|
this->include->remove_at(this->include, enumerator);
|
|
entry_destroy(entry);
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->mutex->unlock(this->mutex);
|
|
ts->destroy(ts);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Create a unique shunt name for a bypass policy
|
|
*/
|
|
static void create_shunt_name(ike_sa_t *ike_sa, traffic_selector_t *ts,
|
|
char *buf, size_t len)
|
|
{
|
|
snprintf(buf, len, "Unity (%s[%u]: %R)", ike_sa->get_name(ike_sa),
|
|
ike_sa->get_unique_id(ike_sa), ts);
|
|
}
|
|
|
|
/**
|
|
* Install entry as a shunt policy
|
|
*/
|
|
static job_requeue_t add_exclude_async(entry_t *entry)
|
|
{
|
|
enumerator_t *enumerator;
|
|
child_cfg_t *child_cfg;
|
|
lifetime_cfg_t lft = {};
|
|
ike_sa_t *ike_sa;
|
|
char name[128];
|
|
host_t *host;
|
|
bool has_vip = FALSE;
|
|
|
|
ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
|
|
entry->sa, FALSE);
|
|
if (ike_sa)
|
|
{
|
|
create_shunt_name(ike_sa, entry->ts, name, sizeof(name));
|
|
|
|
child_cfg = child_cfg_create(name, &lft, NULL, TRUE, MODE_PASS,
|
|
ACTION_NONE, ACTION_NONE, ACTION_NONE,
|
|
FALSE, 0, 0, NULL, NULL, FALSE);
|
|
child_cfg->add_traffic_selector(child_cfg, FALSE,
|
|
entry->ts->clone(entry->ts));
|
|
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
|
|
while (enumerator->enumerate(enumerator, &host))
|
|
{
|
|
has_vip = TRUE;
|
|
child_cfg->add_traffic_selector(child_cfg, TRUE,
|
|
traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0));
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (!has_vip)
|
|
{
|
|
host = ike_sa->get_my_host(ike_sa);
|
|
child_cfg->add_traffic_selector(child_cfg, TRUE,
|
|
traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0));
|
|
}
|
|
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
|
|
|
|
charon->shunts->install(charon->shunts, child_cfg);
|
|
child_cfg->destroy(child_cfg);
|
|
|
|
DBG1(DBG_IKE, "installed %N bypass policy for %R",
|
|
configuration_attribute_type_names, UNITY_LOCAL_LAN, entry->ts);
|
|
}
|
|
return JOB_REQUEUE_NONE;
|
|
}
|
|
|
|
/**
|
|
* Add a bypass policy for a given subnet
|
|
*/
|
|
static bool add_exclude(private_unity_handler_t *this, chunk_t subnet)
|
|
{
|
|
traffic_selector_t *ts;
|
|
ike_sa_t *ike_sa;
|
|
entry_t *entry;
|
|
|
|
ike_sa = charon->bus->get_sa(charon->bus);
|
|
if (!ike_sa)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ts = create_ts(subnet);
|
|
if (!ts)
|
|
{
|
|
return FALSE;
|
|
}
|
|
INIT(entry,
|
|
.sa = ike_sa->get_unique_id(ike_sa),
|
|
.ts = ts,
|
|
);
|
|
|
|
/* we can't install the shunt policy yet, as we don't know the virtual IP.
|
|
* Defer installation using an async callback. */
|
|
lib->processor->queue_job(lib->processor, (job_t*)
|
|
callback_job_create((void*)add_exclude_async, entry,
|
|
(void*)entry_destroy, NULL));
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Remove a bypass policy for a given subnet
|
|
*/
|
|
static bool remove_exclude(private_unity_handler_t *this, chunk_t subnet)
|
|
{
|
|
traffic_selector_t *ts;
|
|
ike_sa_t *ike_sa;
|
|
char name[128];
|
|
|
|
ike_sa = charon->bus->get_sa(charon->bus);
|
|
if (!ike_sa)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ts = create_ts(subnet);
|
|
if (!ts)
|
|
{
|
|
return FALSE;
|
|
}
|
|
create_shunt_name(ike_sa, ts, name, sizeof(name));
|
|
DBG1(DBG_IKE, "uninstalling %N bypass policy for %R",
|
|
configuration_attribute_type_names, UNITY_LOCAL_LAN, ts);
|
|
ts->destroy(ts);
|
|
return charon->shunts->uninstall(charon->shunts, name);
|
|
}
|
|
|
|
METHOD(attribute_handler_t, handle, bool,
|
|
private_unity_handler_t *this, identification_t *id,
|
|
configuration_attribute_type_t type, chunk_t data)
|
|
{
|
|
switch (type)
|
|
{
|
|
case UNITY_SPLIT_INCLUDE:
|
|
return add_include(this, data);
|
|
case UNITY_LOCAL_LAN:
|
|
return add_exclude(this, data);
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
METHOD(attribute_handler_t, release, void,
|
|
private_unity_handler_t *this, identification_t *server,
|
|
configuration_attribute_type_t type, chunk_t data)
|
|
{
|
|
switch (type)
|
|
{
|
|
case UNITY_SPLIT_INCLUDE:
|
|
remove_include(this, data);
|
|
break;
|
|
case UNITY_LOCAL_LAN:
|
|
remove_exclude(this, data);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configuration attributes to request
|
|
*/
|
|
static configuration_attribute_type_t attributes[] = {
|
|
UNITY_SPLIT_INCLUDE,
|
|
UNITY_LOCAL_LAN,
|
|
};
|
|
|
|
/**
|
|
* Attribute enumerator implementation
|
|
*/
|
|
typedef struct {
|
|
/** implements enumerator_t */
|
|
enumerator_t public;
|
|
/** position in attributes[] */
|
|
int i;
|
|
} attribute_enumerator_t;
|
|
|
|
METHOD(enumerator_t, enumerate_attributes, bool,
|
|
attribute_enumerator_t *this, configuration_attribute_type_t *type,
|
|
chunk_t *data)
|
|
{
|
|
if (this->i < countof(attributes))
|
|
{
|
|
*type = attributes[this->i++];
|
|
*data = chunk_empty;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
|
|
unity_handler_t *this, identification_t *id, linked_list_t *vips)
|
|
{
|
|
attribute_enumerator_t *enumerator;
|
|
|
|
INIT(enumerator,
|
|
.public = {
|
|
.enumerate = (void*)_enumerate_attributes,
|
|
.destroy = (void*)free,
|
|
},
|
|
);
|
|
return &enumerator->public;
|
|
}
|
|
|
|
typedef struct {
|
|
/** mutex to unlock */
|
|
mutex_t *mutex;
|
|
/** IKE_SA ID to filter for */
|
|
u_int32_t id;
|
|
} include_filter_t;
|
|
|
|
/**
|
|
* Include enumerator filter function
|
|
*/
|
|
static bool include_filter(include_filter_t *data,
|
|
entry_t **entry, traffic_selector_t **ts)
|
|
{
|
|
if ((*entry)->sa == data->id)
|
|
{
|
|
*ts = (*entry)->ts;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Destroy include filter data, unlock mutex
|
|
*/
|
|
static void destroy_filter(include_filter_t *data)
|
|
{
|
|
data->mutex->unlock(data->mutex);
|
|
free(data);
|
|
}
|
|
|
|
METHOD(unity_handler_t, create_include_enumerator, enumerator_t*,
|
|
private_unity_handler_t *this, u_int32_t id)
|
|
{
|
|
include_filter_t *data;
|
|
|
|
INIT(data,
|
|
.mutex = this->mutex,
|
|
.id = id,
|
|
);
|
|
data->mutex->lock(data->mutex);
|
|
return enumerator_create_filter(
|
|
this->include->create_enumerator(this->include),
|
|
(void*)include_filter, data, (void*)destroy_filter);
|
|
}
|
|
|
|
METHOD(unity_handler_t, destroy, void,
|
|
private_unity_handler_t *this)
|
|
{
|
|
this->include->destroy(this->include);
|
|
this->mutex->destroy(this->mutex);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
unity_handler_t *unity_handler_create()
|
|
{
|
|
private_unity_handler_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.handler = {
|
|
.handle = _handle,
|
|
.release = _release,
|
|
.create_attribute_enumerator = _create_attribute_enumerator,
|
|
},
|
|
.create_include_enumerator = _create_include_enumerator,
|
|
.destroy = _destroy,
|
|
},
|
|
.include = linked_list_create(),
|
|
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
);
|
|
|
|
return &this->public;
|
|
}
|