Add Cisco Unity client support for Split-Include and Local-LAN

This commit is contained in:
Martin Willi 2012-07-23 17:14:47 +02:00
parent 56ea95195a
commit 5ff012f717
9 changed files with 785 additions and 0 deletions

View File

@ -206,6 +206,7 @@ ARG_ENABL_SET([ctr], [enables the Counter Mode wrapper crypto plugin.
ARG_ENABL_SET([ccm], [enables the CCM AEAD wrapper crypto plugin.])
ARG_ENABL_SET([gcm], [enables the GCM AEAD wrapper crypto plugin.])
ARG_ENABL_SET([addrblock], [enables RFC 3779 address block constraint support.])
ARG_ENABL_SET([unity], [enables Cisco Unity extension plugin.])
ARG_ENABL_SET([uci], [enable OpenWRT UCI configuration plugin.])
ARG_ENABL_SET([android], [enable Android specific plugin.])
ARG_ENABL_SET([android-log], [enable Android specific logger plugin.])
@ -953,6 +954,7 @@ ADD_PLUGIN([radattr], [c charon])
ADD_PLUGIN([maemo], [c charon])
ADD_PLUGIN([uci], [c charon])
ADD_PLUGIN([addrblock], [c charon])
ADD_PLUGIN([unity], [c charon])
ADD_PLUGIN([unit-tester], [c charon])
AC_SUBST(charon_plugins)
@ -1078,6 +1080,7 @@ AM_CONDITIONAL(USE_SOCKET_DEFAULT, test x$socket_default = xtrue)
AM_CONDITIONAL(USE_SOCKET_DYNAMIC, test x$socket_dynamic = xtrue)
AM_CONDITIONAL(USE_FARP, test x$farp = xtrue)
AM_CONDITIONAL(USE_ADDRBLOCK, test x$addrblock = xtrue)
AM_CONDITIONAL(USE_UNITY, test x$unity = xtrue)
dnl hydra plugins
dnl =============
@ -1257,6 +1260,7 @@ AC_OUTPUT(
src/libcharon/plugins/medsrv/Makefile
src/libcharon/plugins/medcli/Makefile
src/libcharon/plugins/addrblock/Makefile
src/libcharon/plugins/unity/Makefile
src/libcharon/plugins/uci/Makefile
src/libcharon/plugins/ha/Makefile
src/libcharon/plugins/whitelist/Makefile

View File

@ -533,6 +533,13 @@ if MONOLITHIC
endif
endif
if USE_UNITY
SUBDIRS += plugins/unity
if MONOLITHIC
libcharon_la_LIBADD += plugins/unity/libstrongswan-unity.la
endif
endif
if USE_UNIT_TESTS
SUBDIRS += plugins/unit_tester
if MONOLITHIC

View File

@ -0,0 +1,18 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
-I$(top_srcdir)/src/libcharon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-unity.la
else
plugin_LTLIBRARIES = libstrongswan-unity.la
endif
libstrongswan_unity_la_SOURCES = \
unity_plugin.h unity_plugin.c \
unity_handler.h unity_handler.c \
unity_narrow.h unity_narrow.c
libstrongswan_unity_la_LDFLAGS = -module -avoid-version

View File

@ -0,0 +1,426 @@
/*
* 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;
}

View File

@ -0,0 +1,58 @@
/*
* 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.
*/
/**
* @defgroup unity_handler unity_handler
* @{ @ingroup unity
*/
#ifndef UNITY_HANDLER_H_
#define UNITY_HANDLER_H_
#include <attributes/attribute_handler.h>
typedef struct unity_handler_t unity_handler_t;
/**
* Cisco Unity attribute handling.
*/
struct unity_handler_t {
/**
* Implements attribute_handler_t.
*/
attribute_handler_t handler;
/**
* Create an enumerator over Split-Include attributes received for an IKE_SA.
*
* @param id IKE_SA unique ID to get Split-Includes for
* @return enumerator over traffic_selector_t*
*/
enumerator_t* (*create_include_enumerator)(unity_handler_t *this,
u_int32_t id);
/**
* Destroy a unity_handler_t.
*/
void (*destroy)(unity_handler_t *this);
};
/**
* Create a unity_handler instance.
*/
unity_handler_t *unity_handler_create();
#endif /** UNITY_HANDLER_H_ @}*/

View File

@ -0,0 +1,94 @@
/*
* 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_narrow.h"
#include <daemon.h>
typedef struct private_unity_narrow_t private_unity_narrow_t;
/**
* Private data of an unity_narrow_t object.
*/
struct private_unity_narrow_t {
/**
* Public unity_narrow_t interface.
*/
unity_narrow_t public;
/**
* Unity attribute handler
*/
unity_handler_t *handler;
};
METHOD(listener_t, narrow, bool,
private_unity_narrow_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
narrow_hook_t type, linked_list_t *local, linked_list_t *remote)
{
traffic_selector_t *current, *orig = NULL;
enumerator_t *enumerator;
if (type == NARROW_INITIATOR_POST_AUTH &&
remote->get_count(remote) == 1)
{
enumerator = this->handler->create_include_enumerator(this->handler,
ike_sa->get_unique_id(ike_sa));
while (enumerator->enumerate(enumerator, &current))
{
if (orig == NULL)
{ /* got one, replace original TS */
remote->remove_first(remote, (void**)&orig);
}
remote->insert_last(remote, orig->get_subset(orig, current));
}
enumerator->destroy(enumerator);
if (orig)
{
DBG1(DBG_CFG, "narrowed CHILD_SA to %N %#R",
configuration_attribute_type_names,
UNITY_SPLIT_INCLUDE, remote);
orig->destroy(orig);
}
}
return TRUE;
}
METHOD(unity_narrow_t, destroy, void,
private_unity_narrow_t *this)
{
free(this);
}
/**
* See header
*/
unity_narrow_t *unity_narrow_create(unity_handler_t *handler)
{
private_unity_narrow_t *this;
INIT(this,
.public = {
.listener = {
.narrow = _narrow,
},
.destroy = _destroy,
},
.handler = handler,
);
return &this->public;
}

View File

@ -0,0 +1,51 @@
/*
* 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.
*/
/**
* @defgroup unity_narrow unity_narrow
* @{ @ingroup unity
*/
#ifndef UNITY_NARROW_H_
#define UNITY_NARROW_H_
#include <bus/listeners/listener.h>
#include "unity_handler.h"
typedef struct unity_narrow_t unity_narrow_t;
/**
* Listener that narrows Quick Modes to the Unity Split-Include subnets.
*/
struct unity_narrow_t {
/**
* Implements listener_t.
*/
listener_t listener;
/**
* Destroy a unity_narrow_t.
*/
void (*destroy)(unity_narrow_t *this);
};
/**
* Create a unity_narrow instance.
*/
unity_narrow_t *unity_narrow_create(unity_handler_t *handler);
#endif /** UNITY_NARROW_H_ @}*/

View File

@ -0,0 +1,85 @@
/*
* 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_plugin.h"
#include "unity_handler.h"
#include "unity_narrow.h"
#include <daemon.h>
#include <hydra.h>
typedef struct private_unity_plugin_t private_unity_plugin_t;
/**
* private data of unity_plugin
*/
struct private_unity_plugin_t {
/**
* public functions
*/
unity_plugin_t public;
/**
* Handler for UNITY configuration attributes
*/
unity_handler_t *handler;
/**
* Traffic selector narrower, for Unity Split-Includes
*/
unity_narrow_t *narrower;
};
METHOD(plugin_t, get_name, char*,
private_unity_plugin_t *this)
{
return "unity";
}
METHOD(plugin_t, destroy, void,
private_unity_plugin_t *this)
{
charon->bus->remove_listener(charon->bus, &this->narrower->listener);
this->narrower->destroy(this->narrower);
hydra->attributes->remove_handler(hydra->attributes, &this->handler->handler);
this->handler->destroy(this->handler);
free(this);
}
/*
* see header file
*/
plugin_t *unity_plugin_create()
{
private_unity_plugin_t *this;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
.reload = (void*)return_false,
.destroy = _destroy,
},
},
.handler = unity_handler_create(),
);
hydra->attributes->add_handler(hydra->attributes, &this->handler->handler);
this->narrower = unity_narrow_create(this->handler),
charon->bus->add_listener(charon->bus, &this->narrower->listener);
return &this->public.plugin;
}

View File

@ -0,0 +1,42 @@
/*
* 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.
*/
/**
* @defgroup unity unity
* @ingroup cplugins
*
* @defgroup unity_plugin unity_plugin
* @{ @ingroup unity
*/
#ifndef UNITY_PLUGIN_H_
#define UNITY_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct unity_plugin_t unity_plugin_t;
/**
* IKEv1 Cisco Unity extension support.
*/
struct unity_plugin_t {
/**
* Implements plugin_t. interface.
*/
plugin_t plugin;
};
#endif /** UNITY_PLUGIN_H_ @}*/