vici: Certification Authority support added.

CDP and OCSP URIs for a one or multiple certification authorities
can be added via the VICI interface. swanctl allows to read
definitions from a new authorities section.
This commit is contained in:
Andreas Steffen 2015-07-14 14:41:27 +02:00
parent e194349148
commit 63d370387d
19 changed files with 1553 additions and 15 deletions

6
NEWS
View File

@ -1,3 +1,6 @@
strongswan-5.3.3
----------------
- Added support for the ChaCha20/Poly1305 AEAD cipher specified in RFC7539 and
draft-ietf-ipsecme-chacha20-poly1305 using the chacha20poly1305 ike/esp
proposal keyword. The new chapoly plugin implements the cipher, optionally
@ -5,6 +8,9 @@
strongSwan libipsec ESP backend. On Linux 4.2 or newer the kernel-netlink
plugin can configure the cipher for ESP SAs.
- The vici interface now supports the configuration of auxiliary certification
authority information as CRL and OCSP URIs
strongswan-5.3.2
----------------

View File

@ -23,6 +23,7 @@ libstrongswan_vici_la_SOURCES = \
vici_config.h vici_config.c \
vici_cred.h vici_cred.c \
vici_attribute.h vici_attribute.c \
vici_authority.h vici_authority.c \
vici_logger.h vici_logger.c \
vici_plugin.h vici_plugin.c

View File

@ -366,6 +366,27 @@ over vici.
# completes after streaming list-cert events
}
### list-authorities() ###
List currently loaded certification authority information by streaming
_list-authority_ events.
{
name = <list certification authority of a given name>
} => {
# completes after streaming list-authority events
}
### get-authorities() ###
Return a list of currently loaded certification authority names.
{} => {
authorities = [
<list of certification authority names>
]
}
### load-conn() ###
Load a single connection definition into the daemon. An existing connection
@ -442,6 +463,32 @@ credential cache.
errmsg = <error string on failure>
}
### load-authority() ###
Load a single certification authority definition into the daemon. An existing
authority with the same name gets replaced.
{
<certification authority name> = {
# certification authority parameters
# refer to swanctl.conf(5) for details.
} => {
success = <yes or no>
errmsg = <error string on failure>
}
}
### unload-authority() ###
Unload a previously loaded certification authority definition by name.
{
name = <certification authority name>
} => {
success = <yes or no>
errmsg = <error string on failure>
}
### load-pool() ###
Load an in-memory virtual IP and configuration attribute pool. Existing
@ -673,6 +720,23 @@ _list-certs_ command.
data = <ASN1 encoded certificate data>
}
### list-authority ###
The _list-authority_ event is issued to stream loaded certification authority
information during an active_list-authorities_ command.
{
<certification authority name> = {
cacert = <subject distinguished name of CA certificate>
crl_uris = [
<CRL URI (http, ldap or file)>
]
ocsp_uris = [
<OCSP URI (http)>
]
cert_uri_base = <base URI for download of hash-and-URL certificates>
}
}
# libvici C client library #

View File

@ -0,0 +1,750 @@
/*
* Copyright (C) 2015 Andreas Steffen
* 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.
*/
#define _GNU_SOURCE
#include "vici_authority.h"
#include "vici_builder.h"
#include <threading/rwlock.h>
#include <collections/linked_list.h>
#include <credentials/certificates/x509.h>
#include <utils/debug.h>
#include <stdio.h>
typedef struct private_vici_authority_t private_vici_authority_t;
/**
* Private data of an vici_authority_t object.
*/
struct private_vici_authority_t {
/**
* Public vici_authority_t interface.
*/
vici_authority_t public;
/**
* Dispatcher
*/
vici_dispatcher_t *dispatcher;
/**
* credential backend managed by VICI used for our ca certificates
*/
vici_cred_t *cred;
/**
* List of certification authorities
*/
linked_list_t *authorities;
/**
* rwlock to lock access to certification authorities
*/
rwlock_t *lock;
};
typedef struct authority_t authority_t;
/**
* loaded certification authorities
*/
struct authority_t {
/**
* Name of the certification authoritiy
*/
char *name;
/**
* Reference to CA certificate
*/
certificate_t *cert;
/**
* CRL URIs
*/
linked_list_t *crl_uris;
/**
* OCSP URIs
*/
linked_list_t *ocsp_uris;
/**
* Hashes of certificates issued by this CA
*/
linked_list_t *hashes;
/**
* Base URI used for certificates from this CA
*/
char *cert_uri_base;
};
/**
* create a new certification authority
*/
static authority_t *authority_create(char *name)
{
authority_t *authority;
INIT(authority,
.name = strdup(name),
.crl_uris = linked_list_create(),
.ocsp_uris = linked_list_create(),
.hashes = linked_list_create(),
);
return authority;
}
/**
* destroy a certification authority
*/
static void authority_destroy(authority_t *this)
{
this->crl_uris->destroy_function(this->crl_uris, free);
this->ocsp_uris->destroy_function(this->ocsp_uris, free);
this->hashes->destroy_offset(this->hashes, offsetof(identification_t, destroy));
DESTROY_IF(this->cert);
free(this->cert_uri_base);
free(this->name);
free(this);
}
/**
* Create a (error) reply message
*/
static vici_message_t* create_reply(char *fmt, ...)
{
vici_builder_t *builder;
va_list args;
builder = vici_builder_create();
builder->add_kv(builder, "success", fmt ? "no" : "yes");
if (fmt)
{
va_start(args, fmt);
builder->vadd_kv(builder, "errmsg", fmt, args);
va_end(args);
}
return builder->finalize(builder);
}
/**
* A rule to parse a key/value or list item
*/
typedef struct {
/** name of the key/value or list */
char *name;
/** function to parse value */
bool (*parse)(void *out, chunk_t value);
/** result, passed to parse() */
void *out;
} parse_rule_t;
/**
* Parse key/values using a rule-set
*/
static bool parse_rules(parse_rule_t *rules, int count, char *name,
chunk_t value, vici_message_t **reply)
{
int i;
for (i = 0; i < count; i++)
{
if (streq(name, rules[i].name))
{
if (rules[i].parse(rules[i].out, value))
{
return TRUE;
}
*reply = create_reply("invalid value for: %s, authority discarded",
name);
return FALSE;
}
}
*reply = create_reply("unknown option: %s, authority discarded", name);
return FALSE;
}
/**
* Parse callback data, passed to each callback
*/
typedef struct {
private_vici_authority_t *this;
vici_message_t *reply;
} request_data_t;
/**
* Data associated with an authority load
*/
typedef struct {
request_data_t *request;
authority_t *authority;
} load_data_t;
/**
* Parse a string
*/
CALLBACK(parse_string, bool,
char **str, chunk_t v)
{
if (!chunk_printable(v, NULL, ' '))
{
return FALSE;
}
*str = strndup(v.ptr, v.len);
return TRUE;
}
/**
* Parse list of URIs
*/
CALLBACK(parse_uris, bool,
linked_list_t *out, chunk_t v)
{
char *uri;
if (!chunk_printable(v, NULL, ' '))
{
return FALSE;
}
uri = strndup(v.ptr, v.len);
out->insert_last(out, uri);
return TRUE;
}
/**
* Parse a CA certificate
*/
CALLBACK(parse_cacert, bool,
certificate_t **cacert, chunk_t v)
{
certificate_t *cert;
x509_t *x509;
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB_PEM, v, BUILD_END);
if (!cert)
{
return create_reply("parsing %N certificate failed",
certificate_type_names, CERT_X509);
}
x509 = (x509_t*)cert;
if ((x509->get_flags(x509) & X509_CA) != X509_CA)
{
cert->destroy(cert);
return create_reply("certificate without CA flag, rejected");
}
*cacert = cert;
return TRUE;
}
CALLBACK(authority_kv, bool,
load_data_t *data, vici_message_t *message, char *name, chunk_t value)
{
parse_rule_t rules[] = {
{ "cacert", parse_cacert, &data->authority->cert },
{ "cert_uri_base", parse_string, &data->authority->cert_uri_base },
};
return parse_rules(rules, countof(rules), name, value,
&data->request->reply);
}
CALLBACK(authority_li, bool,
load_data_t *data, vici_message_t *message, char *name, chunk_t value)
{
parse_rule_t rules[] = {
{ "crl_uris", parse_uris, data->authority->crl_uris },
{ "ocsp_uris", parse_uris, data->authority->ocsp_uris },
};
return parse_rules(rules, countof(rules), name, value,
&data->request->reply);
}
static void log_authority_data(authority_t *authority)
{
enumerator_t *enumerator;
identification_t *subject;
bool first = TRUE;
char *uri;
subject = authority->cert->get_subject(authority->cert);
DBG2(DBG_CFG, " cacert = %Y", subject);
enumerator = authority->crl_uris->create_enumerator(authority->crl_uris);
while (enumerator->enumerate(enumerator, &uri))
{
if (first)
{
DBG2(DBG_CFG, " crl_uris = %s", uri);
first = FALSE;
}
else
{
DBG2(DBG_CFG, " %s", uri);
}
}
enumerator->destroy(enumerator);
first = TRUE;
enumerator = authority->ocsp_uris->create_enumerator(authority->ocsp_uris);
while (enumerator->enumerate(enumerator, &uri))
{
if (first)
{
DBG2(DBG_CFG, " ocsp_uris = %s", uri);
first = FALSE;
}
else
{
DBG2(DBG_CFG, " %s", uri);
}
}
enumerator->destroy(enumerator);
if (authority->cert_uri_base)
{
DBG2(DBG_CFG, " cert_uri_base = %s", authority->cert_uri_base);
}
}
CALLBACK(authority_sn, bool,
request_data_t *request, vici_message_t *message,
vici_parse_context_t *ctx, char *name)
{
enumerator_t *enumerator;
linked_list_t *authorities;
authority_t *authority;
vici_cred_t *cred;
load_data_t data = {
.request = request,
.authority = authority_create(name),
};
DBG2(DBG_CFG, " authority %s:", name);
if (!message->parse(message, ctx, NULL, authority_kv, authority_li, &data) ||
!data.authority->cert)
{
authority_destroy(data.authority);
return FALSE;
}
log_authority_data(data.authority);
request->this->lock->write_lock(request->this->lock);
authorities = request->this->authorities;
enumerator = authorities->create_enumerator(authorities);
while (enumerator->enumerate(enumerator, &authority))
{
if (streq(authority->name, name))
{
/* remove the old authority definition */
authorities->remove_at(authorities, enumerator);
authority_destroy(authority);
break;
}
}
enumerator->destroy(enumerator);
authorities->insert_last(authorities, data.authority);
cred = request->this->cred;
data.authority->cert = cred->add_cert(cred, data.authority->cert);
request->this->lock->unlock(request->this->lock);
return TRUE;
}
CALLBACK(load_authority, vici_message_t*,
private_vici_authority_t *this, char *name, u_int id, vici_message_t *message)
{
request_data_t request = {
.this = this,
};
if (!message->parse(message, NULL, authority_sn, NULL, NULL, &request))
{
if (request.reply)
{
return request.reply;
}
return create_reply("parsing request failed");
}
return create_reply(NULL);
}
CALLBACK(unload_authority, vici_message_t*,
private_vici_authority_t *this, char *name, u_int id, vici_message_t *message)
{
enumerator_t *enumerator;
authority_t *authority;
char *authority_name;
bool found = FALSE;
authority_name = message->get_str(message, NULL, "name");
if (!authority_name)
{
return create_reply("unload: missing authority name");
}
this->lock->write_lock(this->lock);
enumerator = this->authorities->create_enumerator(this->authorities);
while (enumerator->enumerate(enumerator, &authority))
{
if (streq(authority->name, authority_name))
{
this->authorities->remove_at(this->authorities, enumerator);
authority_destroy(authority);
found = TRUE;
break;
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
if (!found)
{
return create_reply("unload: authority '%s' not found", authority_name);
}
return create_reply(NULL);
}
CALLBACK(get_authorities, vici_message_t*,
private_vici_authority_t *this, char *name, u_int id,
vici_message_t *message)
{
vici_builder_t *builder;
enumerator_t *enumerator;
authority_t *authority;
builder = vici_builder_create();
builder->begin_list(builder, "authorities");
this->lock->read_lock(this->lock);
enumerator = this->authorities->create_enumerator(this->authorities);
while (enumerator->enumerate(enumerator, &authority))
{
builder->add_li(builder, "%s", authority->name);
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
builder->end_list(builder);
return builder->finalize(builder);
}
CALLBACK(list_authorities, vici_message_t*,
private_vici_authority_t *this, char *name, u_int id, vici_message_t *request)
{
enumerator_t *enumerator, *e;
authority_t *authority;
vici_builder_t *b;
char *str, *uri;
str = request->get_str(request, NULL, "name");
this->lock->read_lock(this->lock);
enumerator = this->authorities->create_enumerator(this->authorities);
while (enumerator->enumerate(enumerator, &authority))
{
if (str && !streq(str, authority->name))
{
continue;
}
b = vici_builder_create();
/* open authority section */
b->begin_section(b, authority->name);
/* subject DN of cacert */
b->add_kv(b, "cacert", "%Y",
authority->cert->get_subject(authority->cert));
/* list of crl_uris */
b->begin_list(b, "crl_uris");
e = authority->crl_uris->create_enumerator(authority->crl_uris);
while (e->enumerate(e, &uri))
{
b->add_li(b, "%s", uri);
}
e->destroy(e);
b->end_list(b);
/* list of ocsp_uris */
b->begin_list(b, "ocsp_uris");
e = authority->ocsp_uris->create_enumerator(authority->ocsp_uris);
while (e->enumerate(e, &uri))
{
b->add_li(b, "%s", uri);
}
e->destroy(e);
b->end_list(b);
/* cert_uri_base */
if (authority->cert_uri_base)
{
b->add_kv(b, "cert_uri_base", "%s", authority->cert_uri_base);
}
/* close authority and raise event */
b->end_section(b);
this->dispatcher->raise_event(this->dispatcher, "list-authority", id,
b->finalize(b));
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
b = vici_builder_create();
return b->finalize(b);
}
static void manage_command(private_vici_authority_t *this,
char *name, vici_command_cb_t cb, bool reg)
{
this->dispatcher->manage_command(this->dispatcher, name,
reg ? cb : NULL, this);
}
/**
* (Un-)register dispatcher functions
*/
static void manage_commands(private_vici_authority_t *this, bool reg)
{
this->dispatcher->manage_event(this->dispatcher, "list-authority", reg);
manage_command(this, "load-authority", load_authority, reg);
manage_command(this, "unload-authority", unload_authority, reg);
manage_command(this, "get-authorities", get_authorities, reg);
manage_command(this, "list-authorities", list_authorities, reg);
}
/**
* data to pass to create_inner_cdp
*/
typedef struct {
private_vici_authority_t *this;
certificate_type_t type;
identification_t *id;
} cdp_data_t;
/**
* destroy cdp enumerator data and unlock list
*/
static void cdp_data_destroy(cdp_data_t *data)
{
data->this->lock->unlock(data->this->lock);
free(data);
}
/**
* inner enumerator constructor for CDP URIs
*/
static enumerator_t *create_inner_cdp(authority_t *authority, cdp_data_t *data)
{
public_key_t *public;
enumerator_t *enumerator = NULL;
linked_list_t *list;
if (data->type == CERT_X509_OCSP_RESPONSE)
{
list = authority->ocsp_uris;
}
else
{
list = authority->crl_uris;
}
public = authority->cert->get_public_key(authority->cert);
if (public)
{
if (!data->id)
{
enumerator = list->create_enumerator(list);
}
else
{
if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
{
enumerator = list->create_enumerator(list);
}
}
public->destroy(public);
}
return enumerator;
}
/**
* inner enumerator constructor for "Hash and URL"
*/
static enumerator_t *create_inner_cdp_hashandurl(authority_t *authority,
cdp_data_t *data)
{
enumerator_t *enumerator = NULL, *hash_enum;
identification_t *current;
if (!data->id || !authority->cert_uri_base)
{
return NULL;
}
hash_enum = authority->hashes->create_enumerator(authority->hashes);
while (hash_enum->enumerate(hash_enum, &current))
{
if (current->matches(current, data->id))
{
char *url, *hash;
url = malloc(strlen(authority->cert_uri_base) + 40 + 1);
strcpy(url, authority->cert_uri_base);
hash = chunk_to_hex(current->get_encoding(current), NULL, FALSE).ptr;
strncat(url, hash, 40);
free(hash);
enumerator = enumerator_create_single(url, free);
break;
}
}
hash_enum->destroy(hash_enum);
return enumerator;
}
METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
private_vici_authority_t *this, certificate_type_t type,
identification_t *id)
{
cdp_data_t *data;
switch (type)
{ /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
case CERT_X509:
case CERT_X509_CRL:
case CERT_X509_OCSP_RESPONSE:
case CERT_ANY:
break;
default:
return NULL;
}
data = malloc_thing(cdp_data_t);
data->this = this;
data->type = type;
data->id = id;
this->lock->read_lock(this->lock);
return enumerator_create_nested(
this->authorities->create_enumerator(this->authorities),
(type == CERT_X509) ? (void*)create_inner_cdp_hashandurl :
(void*)create_inner_cdp, data, (void*)cdp_data_destroy);
}
METHOD(vici_authority_t, check_for_hash_and_url, void,
private_vici_authority_t *this, certificate_t* cert)
{
authority_t *authority;
enumerator_t *enumerator;
hasher_t *hasher;
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (hasher == NULL)
{
DBG1(DBG_CFG, "unable to use hash-and-url: sha1 not supported");
return;
}
this->lock->write_lock(this->lock);
enumerator = this->authorities->create_enumerator(this->authorities);
while (enumerator->enumerate(enumerator, &authority))
{
if (authority->cert_uri_base &&
cert->issued_by(cert, authority->cert, NULL))
{
chunk_t hash, encoded;
if (cert->get_encoding(cert, CERT_ASN1_DER, &encoded))
{
if (hasher->allocate_hash(hasher, encoded, &hash))
{
authority->hashes->insert_last(authority->hashes,
identification_create_from_encoding(ID_KEY_ID, hash));
chunk_free(&hash);
}
chunk_free(&encoded);
}
break;
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
hasher->destroy(hasher);
}
METHOD(vici_authority_t, destroy, void,
private_vici_authority_t *this)
{
manage_commands(this, FALSE);
this->authorities->destroy_function(this->authorities,
(void*)authority_destroy);
this->lock->destroy(this->lock);
free(this);
}
/**
* See header
*/
vici_authority_t *vici_authority_create(vici_dispatcher_t *dispatcher,
vici_cred_t *cred)
{
private_vici_authority_t *this;
INIT(this,
.public = {
.set = {
.create_private_enumerator = (void*)return_null,
.create_cert_enumerator = (void*)return_null,
.create_shared_enumerator = (void*)return_null,
.create_cdp_enumerator = _create_cdp_enumerator,
.cache_cert = (void*)nop,
},
.check_for_hash_and_url = _check_for_hash_and_url,
.destroy = _destroy,
},
.dispatcher = dispatcher,
.cred = cred,
.authorities = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
);
manage_commands(this, TRUE);
return &this->public;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2015 Andreas Steffen
* 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.
*/
/**
* @defgroup vici_authority vici_authority
* @{ @ingroup vici
*/
#ifndef VICI_AUTHORITY_H_
#define VICI_AUTHORITY_H_
#include "vici_dispatcher.h"
#include "vici_cred.h"
typedef struct vici_authority_t vici_authority_t;
/**
* In-memory certification authority backend, managed by VICI.
*/
struct vici_authority_t {
/**
* Implements credential_set_t
*/
credential_set_t set;
/**
* Check if a certificate can be made available through hash and URL.
*
* @param cert end entity certificate
*/
void (*check_for_hash_and_url)(vici_authority_t *this, certificate_t* cert);
/**
* Destroy a vici_authority_t.
*/
void (*destroy)(vici_authority_t *this);
};
/**
* Create a vici_authority instance.
*
* @param dispatcher dispatcher to receive requests from
* @param cred in-memory credential backend managed by VICI
* @return authority backend
*/
vici_authority_t *vici_authority_create(vici_dispatcher_t *dispatcher,
vici_cred_t *cred);
#endif /** VICI_AUTHORITY_H_ @}*/

View File

@ -96,6 +96,12 @@ struct private_vici_config_t {
* Lock for conns list
*/
rwlock_t *lock;
/**
* Auxiliary certification authority information
*/
vici_authority_t *authority;
};
METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
@ -1043,15 +1049,21 @@ CALLBACK(parse_group, bool,
/**
* Parse a certificate; add as auth rule to config
*/
static bool parse_cert(auth_cfg_t *cfg, auth_rule_t rule, chunk_t v)
static bool parse_cert(auth_data_t *auth, auth_rule_t rule, chunk_t v)
{
vici_authority_t *authority;
certificate_t *cert;
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB_PEM, v, BUILD_END);
if (cert)
{
cfg->add(cfg, rule, cert);
if (rule == AUTH_RULE_SUBJECT_CERT)
{
authority = auth->request->this->authority;
authority->check_for_hash_and_url(authority, cert);
}
auth->cfg->add(auth->cfg, rule, cert);
return TRUE;
}
return FALSE;
@ -1061,18 +1073,18 @@ static bool parse_cert(auth_cfg_t *cfg, auth_rule_t rule, chunk_t v)
* Parse subject certificates
*/
CALLBACK(parse_certs, bool,
auth_cfg_t *cfg, chunk_t v)
auth_data_t *auth, chunk_t v)
{
return parse_cert(cfg, AUTH_RULE_SUBJECT_CERT, v);
return parse_cert(auth, AUTH_RULE_SUBJECT_CERT, v);
}
/**
* Parse CA certificates
*/
CALLBACK(parse_cacerts, bool,
auth_cfg_t *cfg, chunk_t v)
auth_data_t *auth, chunk_t v)
{
return parse_cert(cfg, AUTH_RULE_CA_CERT, v);
return parse_cert(auth, AUTH_RULE_CA_CERT, v);
}
/**
@ -1267,8 +1279,8 @@ CALLBACK(auth_li, bool,
{
parse_rule_t rules[] = {
{ "groups", parse_group, auth->cfg },
{ "certs", parse_certs, auth->cfg },
{ "cacerts", parse_cacerts, auth->cfg },
{ "certs", parse_certs, auth },
{ "cacerts", parse_cacerts, auth },
};
return parse_rules(rules, countof(rules), name, value,
@ -2056,7 +2068,8 @@ METHOD(vici_config_t, destroy, void,
/**
* See header
*/
vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher)
vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher,
vici_authority_t *authority)
{
private_vici_config_t *this;
@ -2072,6 +2085,7 @@ vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher)
.dispatcher = dispatcher,
.conns = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
.authority = authority,
);
manage_commands(this, TRUE);

View File

@ -2,6 +2,9 @@
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* Copyright (C) 2015 Andreas Steffen
* 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
@ -22,6 +25,7 @@
#define VICI_CONFIG_H_
#include "vici_dispatcher.h"
#include "vici_authority.h"
#include <config/backend.h>
@ -46,8 +50,10 @@ struct vici_config_t {
* Create a vici_config instance.
*
* @param dispatcher dispatcher to receive requests from
* @param authority Auxiliary certification authority information
* @return config backend
*/
vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher);
vici_config_t *vici_config_create(vici_dispatcher_t *dispatcher,
vici_authority_t *authority);
#endif /** VICI_CONFIG_H_ @}*/

View File

@ -294,6 +294,12 @@ static void manage_commands(private_vici_cred_t *this, bool reg)
manage_command(this, "load-shared", load_shared, reg);
}
METHOD(vici_cred_t, add_cert, certificate_t*,
private_vici_cred_t *this, certificate_t *cert)
{
return this->creds->get_cert_ref(this->creds, cert);
}
METHOD(vici_cred_t, destroy, void,
private_vici_cred_t *this)
{
@ -313,6 +319,7 @@ vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher)
INIT(this,
.public = {
.add_cert = _add_cert,
.destroy = _destroy,
},
.dispatcher = dispatcher,

View File

@ -30,6 +30,14 @@ typedef struct vici_cred_t vici_cred_t;
*/
struct vici_cred_t {
/**
* Add a certificate to the certificate store
*
* @param cert certificate to be added to store
* @return reference to certificate or cached copy
*/
certificate_t* (*add_cert)(vici_cred_t *this, certificate_t *cert);
/**
* Destroy a vici_cred_t.
*/

View File

@ -2,6 +2,9 @@
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* Copyright (C) 2015 Andreas Steffen
* 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
@ -42,6 +45,7 @@
#include "vici_cred.h"
#include "vici_config.h"
#include "vici_attribute.h"
#include "vici_authority.h"
#include "vici_logger.h"
#include <library.h>
@ -79,6 +83,11 @@ struct private_vici_plugin_t {
*/
vici_cred_t *cred;
/**
* Certification Authority backend
*/
vici_authority_t *authority;
/**
* Configuration backend
*/
@ -119,7 +128,10 @@ static bool register_vici(private_vici_plugin_t *this,
this->query = vici_query_create(this->dispatcher);
this->control = vici_control_create(this->dispatcher);
this->cred = vici_cred_create(this->dispatcher);
this->config = vici_config_create(this->dispatcher);
this->authority = vici_authority_create(this->dispatcher,
this->cred);
lib->credmgr->add_set(lib->credmgr, &this->authority->set);
this->config = vici_config_create(this->dispatcher, this->authority);
this->attrs = vici_attribute_create(this->dispatcher);
this->logger = vici_logger_create(this->dispatcher);
@ -145,6 +157,8 @@ static bool register_vici(private_vici_plugin_t *this,
this->logger->destroy(this->logger);
this->attrs->destroy(this->attrs);
this->config->destroy(this->config);
lib->credmgr->remove_set(lib->credmgr, &this->authority->set);
this->authority->destroy(this->authority);
this->cred->destroy(this->cred);
this->control->destroy(this->control);
this->query->destroy(this->query);

View File

@ -7,10 +7,12 @@ swanctl_SOURCES = \
commands/install.c \
commands/list_sas.c \
commands/list_pols.c \
commands/list_authorities.c \
commands/list_conns.c \
commands/list_certs.c \
commands/list_pools.c \
commands/load_all.c \
commands/load_authorities.h commands/load_authorities.c \
commands/load_conns.c commands/load_conns.h \
commands/load_creds.c commands/load_creds.h \
commands/load_pools.c commands/load_pools.h \

View File

@ -211,7 +211,7 @@ int command_usage(char *error, ...)
{
for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
{
fprintf(out, " swanctl --%-15s (-%c) %s\n",
fprintf(out, " swanctl --%-16s (-%c) %s\n",
cmds[i].cmd, cmds[i].op, cmds[i].description);
}
}

View File

@ -27,7 +27,7 @@
/**
* Maximum number of commands (+1).
*/
#define MAX_COMMANDS 19
#define MAX_COMMANDS 21
/**
* Maximum number of options in a command (+3)

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2015 Andreas Steffen
* 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include "command.h"
#define LABELED_CRL_URI (1 << 0)
#define LABELED_OCSP_URI (1 << 1)
CALLBACK(authority_kv, int,
void *null, vici_res_t *res, char *name, void *value, int len)
{
chunk_t chunk;
chunk = chunk_create(value, len);
if (chunk_printable(chunk, NULL, ' '))
{
printf(" %s: %.*s\n", name, len, value);
}
return 0;
}
CALLBACK(authority_list, int,
int *labeled, vici_res_t *res, char *name, void *value, int len)
{
chunk_t chunk;
chunk = chunk_create(value, len);
if (chunk_printable(chunk, NULL, ' '))
{
if (streq(name, "crl_uris"))
{
printf(" %s %.*s\n",
(*labeled & LABELED_CRL_URI) ? " " : "crl_uris: ",
len, value);
*labeled |= LABELED_CRL_URI;
}
if (streq(name, "ocsp_uris"))
{
printf(" %s %.*s\n",
(*labeled & LABELED_OCSP_URI) ? " " : "ocsp_uris:",
len, value);
*labeled %= LABELED_OCSP_URI;
}
}
return 0;
}
CALLBACK(authorities, int,
void *null, vici_res_t *res, char *name)
{
int labeled = 0;
printf("%s:\n", name);
return vici_parse_cb(res, NULL, authority_kv, authority_list, &labeled);
}
CALLBACK(list_cb, void,
command_format_options_t *format, char *name, vici_res_t *res)
{
if (*format & COMMAND_FORMAT_RAW)
{
vici_dump(res, "list-authorities event", *format & COMMAND_FORMAT_PRETTY,
stdout);
}
else
{
if (vici_parse_cb(res, authorities, NULL, NULL, NULL) != 0)
{
fprintf(stderr, "parsing authority event failed: %s\n",
strerror(errno));
}
}
}
static int list_authorities(vici_conn_t *conn)
{
vici_req_t *req;
vici_res_t *res;
command_format_options_t format = COMMAND_FORMAT_NONE;
char *arg, *ca_name = NULL;;
int ret = 0;
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h':
return command_usage(NULL);
case 'n':
ca_name = arg;
continue;
case 'P':
format |= COMMAND_FORMAT_PRETTY;
/* fall through to raw */
case 'r':
format |= COMMAND_FORMAT_RAW;
continue;
case EOF:
break;
default:
return command_usage("invalid --list-authorities option");
}
break;
}
if (vici_register(conn, "list-authority", list_cb, &format) != 0)
{
ret = errno;
fprintf(stderr, "registering for authorities failed: %s\n",
strerror(errno));
return ret;
}
req = vici_begin("list-authorities");
if (ca_name)
{
vici_add_key_valuef(req, "name", "%s", ca_name);
}
res = vici_submit(req, conn);
if (!res)
{
ret = errno;
fprintf(stderr, "list-authorities request failed: %s\n", strerror(errno));
return ret;
}
if (format & COMMAND_FORMAT_RAW)
{
vici_dump(res, "list-authorities reply", format & COMMAND_FORMAT_PRETTY,
stdout);
}
vici_free_res(res);
return 0;
}
/**
* Register the command.
*/
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
list_authorities, 'B', "list-authorities",
"list loaded authority configurations",
{"[--raw|--pretty]"},
{
{"help", 'h', 0, "show usage information"},
{"name", 'n', 1, "filter by authority name"},
{"raw", 'r', 0, "dump raw response message"},
{"pretty", 'P', 0, "dump raw response message in pretty print"},
}
});
}

View File

@ -22,6 +22,7 @@
#include "command.h"
#include "swanctl.h"
#include "load_creds.h"
#include "load_authorities.h"
#include "load_pools.h"
#include "load_conns.h"
@ -71,6 +72,10 @@ static int load_all(vici_conn_t *conn)
ret = load_creds_cfg(conn, format, cfg, clear, noprompt);
}
if (ret == 0)
{
ret = load_authorities_cfg(conn, format, cfg);
}
if (ret == 0)
{
ret = load_pools_cfg(conn, format, cfg);
}
@ -90,7 +95,8 @@ static int load_all(vici_conn_t *conn)
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
load_all, 'q', "load-all", "load credentials, pools and connections",
load_all, 'q', "load-all",
"load credentials, authorities, pools and connections",
{"[--raw|--pretty] [--clear] [--noprompt]"},
{
{"help", 'h', 0, "show usage information"},

View File

@ -0,0 +1,365 @@
/*
* Copyright (C) 2015 Andreas Steffen
* 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include "command.h"
#include "swanctl.h"
#include "load_authorities.h"
/**
* Add a vici list from a comma separated string value
*/
static void add_list_key(vici_req_t *req, char *key, char *value)
{
enumerator_t *enumerator;
char *token;
vici_begin_list(req, key);
enumerator = enumerator_create_token(value, ",", " ");
while (enumerator->enumerate(enumerator, &token))
{
vici_add_list_itemf(req, "%s", token);
}
enumerator->destroy(enumerator);
vici_end_list(req);
}
/**
* Add a vici certificate blob value given by its file patch
*/
static bool add_file_key_value(vici_req_t *req, char *key, char *value)
{
chunk_t *map;
char *path, buf[PATH_MAX];
if (path_absolute(value))
{
path = value;
}
else
{
path = buf;
snprintf(path, PATH_MAX, "%s%s%s",
SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, value);
}
map = chunk_map(path, FALSE);
if (map)
{
vici_add_key_value(req, key, map->ptr, map->len);
chunk_unmap(map);
return TRUE;
}
else
{
fprintf(stderr, "loading ca certificate '%s' failed: %s\n",
path, strerror(errno));
return FALSE;
}
}
/**
* Translate sletting key/values from a section into vici key-values/lists
*/
static bool add_key_values(vici_req_t *req, settings_t *cfg, char *section)
{
enumerator_t *enumerator;
char *key, *value;
bool ret = TRUE;
enumerator = cfg->create_key_value_enumerator(cfg, section);
while (enumerator->enumerate(enumerator, &key, &value))
{
/* pool subnet is encoded as key/value, all other attributes as list */
if (streq(key, "cacert"))
{
ret = add_file_key_value(req, key, value);
}
else if (streq(key, "cert_uri_base"))
{
vici_add_key_valuef(req, key, "%s", value);
}
else
{
add_list_key(req, key, value);
}
if (!ret)
{
break;
}
}
enumerator->destroy(enumerator);
return ret;
}
/**
* Load an authority configuration
*/
static bool load_authority(vici_conn_t *conn, settings_t *cfg,
char *section, command_format_options_t format)
{
vici_req_t *req;
vici_res_t *res;
bool ret = TRUE;
char buf[128];
snprintf(buf, sizeof(buf), "%s.%s", "authorities", section);
req = vici_begin("load-authority");
vici_begin_section(req, section);
if (!add_key_values(req, cfg, buf))
{
vici_free_req(req);
return FALSE;
}
vici_end_section(req);
res = vici_submit(req, conn);
if (!res)
{
fprintf(stderr, "load-authority request failed: %s\n", strerror(errno));
return FALSE;
}
if (format & COMMAND_FORMAT_RAW)
{
vici_dump(res, "load-authority reply", format & COMMAND_FORMAT_PRETTY,
stdout);
}
else if (!streq(vici_find_str(res, "no", "success"), "yes"))
{
fprintf(stderr, "loading authority '%s' failed: %s\n",
section, vici_find_str(res, "", "errmsg"));
ret = FALSE;
}
else
{
printf("loaded authority '%s'\n", section);
}
vici_free_res(res);
return ret;
}
CALLBACK(list_authority, int,
linked_list_t *list, vici_res_t *res, char *name, void *value, int len)
{
if (streq(name, "authorities"))
{
char *str;
if (asprintf(&str, "%.*s", len, value) != -1)
{
list->insert_last(list, str);
}
}
return 0;
}
/**
* Create a list of currently loaded authorities
*/
static linked_list_t* list_authorities(vici_conn_t *conn,
command_format_options_t format)
{
linked_list_t *list;
vici_res_t *res;
list = linked_list_create();
res = vici_submit(vici_begin("get-authorities"), conn);
if (res)
{
if (format & COMMAND_FORMAT_RAW)
{
vici_dump(res, "get-authorities reply", format & COMMAND_FORMAT_PRETTY,
stdout);
}
vici_parse_cb(res, NULL, NULL, list_authority, list);
vici_free_res(res);
}
return list;
}
/**
* Remove and free a string from a list
*/
static void remove_from_list(linked_list_t *list, char *str)
{
enumerator_t *enumerator;
char *current;
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &current))
{
if (streq(current, str))
{
list->remove_at(list, enumerator);
free(current);
}
}
enumerator->destroy(enumerator);
}
/**
* Unload a authority by name
*/
static bool unload_authority(vici_conn_t *conn, char *name,
command_format_options_t format)
{
vici_req_t *req;
vici_res_t *res;
bool ret = TRUE;
req = vici_begin("unload-authority");
vici_add_key_valuef(req, "name", "%s", name);
res = vici_submit(req, conn);
if (!res)
{
fprintf(stderr, "unload-authority request failed: %s\n", strerror(errno));
return FALSE;
}
if (format & COMMAND_FORMAT_RAW)
{
vici_dump(res, "unload-authority reply", format & COMMAND_FORMAT_PRETTY,
stdout);
}
else if (!streq(vici_find_str(res, "no", "success"), "yes"))
{
fprintf(stderr, "unloading authority '%s' failed: %s\n",
name, vici_find_str(res, "", "errmsg"));
ret = FALSE;
}
vici_free_res(res);
return ret;
}
/**
* See header.
*/
int load_authorities_cfg(vici_conn_t *conn, command_format_options_t format,
settings_t *cfg)
{
u_int found = 0, loaded = 0, unloaded = 0;
char *section;
enumerator_t *enumerator;
linked_list_t *authorities;
authorities = list_authorities(conn, format);
enumerator = cfg->create_section_enumerator(cfg, "authorities");
while (enumerator->enumerate(enumerator, &section))
{
remove_from_list(authorities, section);
found++;
if (load_authority(conn, cfg, section, format))
{
loaded++;
}
}
enumerator->destroy(enumerator);
/* unload all authorities in daemon, but not in file */
while (authorities->remove_first(authorities, (void**)&section) == SUCCESS)
{
if (unload_authority(conn, section, format))
{
unloaded++;
}
free(section);
}
authorities->destroy(authorities);
if (format & COMMAND_FORMAT_RAW)
{
return 0;
}
if (found == 0)
{
printf("no authorities found, %u unloaded\n", unloaded);
return 0;
}
if (loaded == found)
{
printf("successfully loaded %u authorities, %u unloaded\n",
loaded, unloaded);
return 0;
}
fprintf(stderr, "loaded %u of %u authorities, %u failed to load, "
"%u unloaded\n", loaded, found, found - loaded, unloaded);
return EINVAL;
}
static int load_authorities(vici_conn_t *conn)
{
command_format_options_t format = COMMAND_FORMAT_NONE;
settings_t *cfg;
char *arg;
int ret;
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h':
return command_usage(NULL);
case 'P':
format |= COMMAND_FORMAT_PRETTY;
/* fall through to raw */
case 'r':
format |= COMMAND_FORMAT_RAW;
continue;
case EOF:
break;
default:
return command_usage("invalid --load-authorities option");
}
break;
}
cfg = settings_create(SWANCTL_CONF);
if (!cfg)
{
fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
return EINVAL;
}
ret = load_authorities_cfg(conn, format, cfg);
cfg->destroy(cfg);
return ret;
}
/**
* Register the command.
*/
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
load_authorities, 'b',
"load-authorities", "(re-)load authority configuration",
{"[--raw|--pretty]"},
{
{"help", 'h', 0, "show usage information"},
{"raw", 'r', 0, "dump raw response message"},
{"pretty", 'P', 0, "dump raw response message in pretty print"},
}
});
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2015 Andreas Stefffen
* 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 "command.h"
/**
* Load all certification authority definitions from configuration file
*
* @param conn vici connection to load to
* @param format output format
* @param cfg configuration to load from
*/
int load_authorities_cfg(vici_conn_t *conn, command_format_options_t format,
settings_t *cfg);

View File

@ -53,9 +53,15 @@ list currently active IKE_SAs
.B "\-P, \-\-list\-pols"
list currently installed policies
.TP
.B "\-b, \-\-load\-authorities"
(re\-)load certification authorities information
.TP
.B "\-L, \-\-list\-conns"
list loaded configurations
.TP
.B "\-B, \-\-list\-authorities"
list loaded certification authorities information
.TP
.B "\-x, \-\-list\-certs"
list stored certificates
.TP
@ -63,7 +69,7 @@ list stored certificates
list loaded pool configurations
.TP
.B "\-q, \-\-load\-all"
(re\-)load credentials, pools and connections
(re\-)load credentials, pools, authorities and connections
.TP
.B "\-c, \-\-load\-conns"
(re\-)load connection configuration

View File

@ -810,3 +810,35 @@ pools.<name>.<attr> =
subnets for the corresponding attribute types. Alternatively, **<attr>** can
be a numerical identifier, for which string attribute values are accepted
as well.
authorities { # }
Section defining attributes of certification authorities.
authorities.<name> { # }
Section defining a certification authority with a unique name.
authorities.<name>.cacert =
CA certificate belonging to the certification authority.
The certificates may use a relative path from the **swanctl** _x509ca_
directory, or an absolute path.
authorities.<name>.crl_uris =
Comma-separated list of CRL distribution points
Comma-separated list of CRL distribution points (ldap, http, or file URI)
authorities.<name>.ocsp_uris =
Comma-separated list of OCSP URIs
Comma-separated list of OCSP URIs
authorities.<name>.cert_uri_base =
Defines the base URI for the Hash and URL feature supported by IKEv2.
Defines the base URI for the Hash and URL feature supported by IKEv2.
Instead of exchanging complete certificates, IKEv2 allows one to send an
URI that resolves to the DER encoded certificate. The certificate URIs are
built by appending the SHA1 hash of the DER encoded certificates to this
base URI.