diff options
Diffstat (limited to 'src/libcharon/plugins')
49 files changed, 2005 insertions, 322 deletions
diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c index 60d12dc1d..237f065fa 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius.c +++ b/src/libcharon/plugins/eap_radius/eap_radius.c @@ -434,6 +434,9 @@ static void add_nameserver_attribute(eap_radius_provider_t *provider, case 31: /* MS-Secondary-NBNS-Server */ provider->add_attribute(provider, id, INTERNAL_IP4_NBNS, data); break; + case RAT_FRAMED_IPV6_DNS_SERVER: + provider->add_attribute(provider, id, INTERNAL_IP6_DNS, data); + break; } } @@ -515,9 +518,10 @@ static void process_cfg_attributes(radius_message_t *msg) enumerator = msg->create_enumerator(msg); while (enumerator->enumerate(enumerator, &type, &data)) { - if (type == RAT_FRAMED_IP_ADDRESS && data.len == 4) + if ((type == RAT_FRAMED_IP_ADDRESS && data.len == 4) || + (type == RAT_FRAMED_IPV6_ADDRESS && data.len == 16)) { - host = host_create_from_chunk(AF_INET, data, 0); + host = host_create_from_chunk(AF_UNSPEC, data, 0); if (host) { provider->add_framed_ip(provider, @@ -529,6 +533,11 @@ static void process_cfg_attributes(radius_message_t *msg) provider->add_attribute(provider, ike_sa->get_unique_id(ike_sa), INTERNAL_IP4_NETMASK, data); } + else if (type == RAT_FRAMED_IPV6_DNS_SERVER && data.len == 16) + { + add_nameserver_attribute(provider, + ike_sa->get_unique_id(ike_sa), type, data); + } } enumerator->destroy(enumerator); diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c index cef19305c..4b7260349 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -447,10 +447,8 @@ static void add_ike_sa_parameters(private_eap_radius_accounting_t *this, vip->get_address(vip)); break; case AF_INET6: - /* we currently assign /128 prefixes, only (reserved, length) */ - data = chunk_from_chars(0, 128); - data = chunk_cata("cc", data, vip->get_address(vip)); - message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + message->add(message, RAT_FRAMED_IPV6_ADDRESS, + vip->get_address(vip)); break; default: break; @@ -694,6 +692,11 @@ static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) entry = get_or_create_entry(this, ike_sa->get_id(ike_sa), ike_sa->get_unique_id(ike_sa)); + if (entry->start_sent) + { + this->mutex->unlock(this->mutex); + return; + } entry->start_sent = TRUE; message = radius_message_create(RMC_ACCOUNTING_REQUEST); @@ -860,11 +863,6 @@ METHOD(listener_t, message_hook, bool, if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && !incoming && !message->get_request(message)) { - if (ike_sa->get_version(ike_sa) == IKEV1 && - message->get_exchange_type(message) == TRANSACTION) - { - send_start(this, ike_sa); - } if (ike_sa->get_version(ike_sa) == IKEV2 && message->get_exchange_type(message) == IKE_AUTH) { @@ -874,6 +872,17 @@ METHOD(listener_t, message_hook, bool, return TRUE; } +METHOD(listener_t, assign_vips, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool assign) +{ + /* start accounting as soon as the virtual IP is set */ + if (assign && ike_sa->get_version(ike_sa) == IKEV1) + { + send_start(this, ike_sa); + } + return TRUE; +} + METHOD(listener_t, ike_rekey, bool, private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new) { @@ -1003,6 +1012,7 @@ eap_radius_accounting_t *eap_radius_accounting_create() .ike_updown = _ike_updown, .ike_rekey = _ike_rekey, .message = _message_hook, + .assign_vips = _assign_vips, .child_updown = _child_updown, .child_rekey = _child_rekey, .children_migrate = _children_migrate, diff --git a/src/libcharon/plugins/eap_tnc/eap_tnc.c b/src/libcharon/plugins/eap_tnc/eap_tnc.c index f70f47ef6..350001bb4 100644 --- a/src/libcharon/plugins/eap_tnc/eap_tnc.c +++ b/src/libcharon/plugins/eap_tnc/eap_tnc.c @@ -335,6 +335,10 @@ static eap_tnc_t *eap_tnc_create(identification_t *server, free(this); return NULL; } + if (!is_server) + { + tnccs->set_auth_type(tnccs, TNC_AUTH_X509_CERT); + } this->tnccs = tnccs->get_ref(tnccs); this->tls_eap = tls_eap_create(type, &tnccs->tls, EAP_TNC_MAX_MESSAGE_LEN, diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c index 66c9deed8..e0b59a681 100644 --- a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c +++ b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c @@ -112,6 +112,13 @@ METHOD(tls_application_t, process, status_t, eap_data = avp_data; break; } + else if (eap_len > reader->remaining(reader) + avp_data.len) + { + /* rough size check, ignoring AVP headers in remaining data */ + DBG1(DBG_IKE, "EAP packet too large for EAP-TTLS AVP(s)"); + chunk_free(&avp_data); + return FAILED; + } else if (avp_data.len == MAX_RADIUS_ATTRIBUTE_SIZE) { /* non-standard: EAP packet segmented into multiple AVPs */ @@ -128,7 +135,7 @@ METHOD(tls_application_t, process, status_t, if (avp_data.len > eap_data.len - eap_pos) { - DBG1(DBG_IKE, "AVP size to large to fit into EAP packet"); + DBG1(DBG_IKE, "AVP size too large to fit into EAP packet"); chunk_free(&avp_data); chunk_free(&eap_data); return FAILED; diff --git a/src/libcharon/plugins/error_notify/error_notify_listener.c b/src/libcharon/plugins/error_notify/error_notify_listener.c index 13860fe50..f7a1f49ec 100644 --- a/src/libcharon/plugins/error_notify/error_notify_listener.c +++ b/src/libcharon/plugins/error_notify/error_notify_listener.c @@ -96,13 +96,13 @@ METHOD(listener_t, alert, bool, case ALERT_PROPOSAL_MISMATCH_IKE: msg.type = htonl(ERROR_NOTIFY_PROPOSAL_MISMATCH_IKE); list = va_arg(args, linked_list_t*); - snprintf(msg.str, sizeof(msg.str), "the received IKE_SA poposals " + snprintf(msg.str, sizeof(msg.str), "the received IKE_SA proposals " "did not match: %#P", list); break; case ALERT_PROPOSAL_MISMATCH_CHILD: msg.type = htonl(ERROR_NOTIFY_PROPOSAL_MISMATCH_CHILD); list = va_arg(args, linked_list_t*); - snprintf(msg.str, sizeof(msg.str), "the received CHILD_SA poposals " + snprintf(msg.str, sizeof(msg.str), "the received CHILD_SA proposals " "did not match: %#P", list); break; case ALERT_TS_MISMATCH: @@ -153,14 +153,14 @@ METHOD(listener_t, alert, bool, msg.type = htonl(ERROR_NOTIFY_CERT_EXPIRED); cert = va_arg(args, certificate_t*); cert->get_validity(cert, NULL, ¬_before, ¬_after); - snprintf(msg.str, sizeof(msg.str), "certificiate expired: '%Y' " + snprintf(msg.str, sizeof(msg.str), "certificate expired: '%Y' " "(valid from %T to %T)", cert->get_subject(cert), ¬_before, TRUE, ¬_after, TRUE); break; case ALERT_CERT_REVOKED: msg.type = htonl(ERROR_NOTIFY_CERT_REVOKED); cert = va_arg(args, certificate_t*); - snprintf(msg.str, sizeof(msg.str), "certificiate revoked: '%Y'", + snprintf(msg.str, sizeof(msg.str), "certificate revoked: '%Y'", cert->get_subject(cert)); break; case ALERT_CERT_NO_ISSUER: diff --git a/src/libcharon/plugins/ha/ha_ctl.c b/src/libcharon/plugins/ha/ha_ctl.c index a95499742..54302e852 100644 --- a/src/libcharon/plugins/ha/ha_ctl.c +++ b/src/libcharon/plugins/ha/ha_ctl.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -51,6 +52,41 @@ struct private_ha_ctl_t { }; /** + * Change the permissions of the control FIFO, returns TRUE on success + */ +static bool change_fifo_permissions() +{ + if (chown(HA_FIFO, lib->caps->get_uid(lib->caps), + lib->caps->get_gid(lib->caps)) != 0) + { + DBG1(DBG_CFG, "changing HA FIFO permissions failed: %s", + strerror(errno)); + return FALSE; + } + return TRUE; +} + +/** + * Deletes and creates the control FIFO, returns TRUE on success + */ +static bool recreate_fifo() +{ + mode_t old; + bool success = TRUE; + + unlink(HA_FIFO); + old = umask(S_IRWXO); + if (mkfifo(HA_FIFO, S_IRUSR | S_IWUSR) != 0) + { + DBG1(DBG_CFG, "creating HA FIFO %s failed: %s", HA_FIFO, + strerror(errno)); + success = FALSE; + } + umask(old); + return success && change_fifo_permissions(); +} + +/** * FIFO dispatching function */ static job_requeue_t dispatch_fifo(private_ha_ctl_t *this) @@ -59,13 +95,26 @@ static job_requeue_t dispatch_fifo(private_ha_ctl_t *this) bool oldstate; char buf[8]; u_int segment; + struct stat sb; oldstate = thread_cancelability(TRUE); fifo = open(HA_FIFO, O_RDONLY); thread_cancelability(oldstate); - if (fifo == -1) + if (fifo == -1 || fstat(fifo, &sb) != 0 || !S_ISFIFO(sb.st_mode)) { - DBG1(DBG_CFG, "opening HA fifo failed: %s", strerror(errno)); + if (fifo == -1 && errno != ENOENT) + { + DBG1(DBG_CFG, "opening HA FIFO failed: %s", strerror(errno)); + } + else + { + DBG1(DBG_CFG, "%s is not a FIFO, recreate it", HA_FIFO); + recreate_fifo(); + } + if (fifo != -1) + { + close(fifo); + } sleep(1); return JOB_REQUEUE_FAIR; } @@ -100,6 +149,7 @@ static job_requeue_t dispatch_fifo(private_ha_ctl_t *this) METHOD(ha_ctl_t, destroy, void, private_ha_ctl_t *this) { + unlink(HA_FIFO); free(this); } @@ -109,7 +159,7 @@ METHOD(ha_ctl_t, destroy, void, ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache) { private_ha_ctl_t *this; - mode_t old; + struct stat sb; INIT(this, .public = { @@ -119,20 +169,30 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache) .cache = cache, ); - if (access(HA_FIFO, R_OK|W_OK) != 0) + if (stat(HA_FIFO, &sb) == 0) { - old = umask(S_IRWXO); - if (mkfifo(HA_FIFO, S_IRUSR | S_IWUSR) != 0) + if (!S_ISFIFO(sb.st_mode)) { - DBG1(DBG_CFG, "creating HA FIFO %s failed: %s", - HA_FIFO, strerror(errno)); + DBG1(DBG_CFG, "%s is not a FIFO, recreate it", HA_FIFO); + recreate_fifo(); + } + else if (access(HA_FIFO, R_OK|W_OK) != 0) + { + DBG1(DBG_CFG, "accessing HA FIFO %s denied, recreate it", HA_FIFO); + recreate_fifo(); + } + else + { + change_fifo_permissions(); } - umask(old); } - if (chown(HA_FIFO, lib->caps->get_uid(lib->caps), - lib->caps->get_gid(lib->caps)) != 0) + else if (errno == ENOENT) { - DBG1(DBG_CFG, "changing HA FIFO permissions failed: %s", + recreate_fifo(); + } + else + { + DBG1(DBG_CFG, "accessing HA FIFO %s failed: %s", HA_FIFO, strerror(errno)); } @@ -141,4 +201,3 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache) this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); return &this->public; } - diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 31eeb934e..afa099309 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -135,6 +135,7 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty; chunk_t secret = chunk_empty, old_skd = chunk_empty; chunk_t dh_local = chunk_empty, dh_remote = chunk_empty, psk = chunk_empty; + host_t *other = NULL; bool ok = FALSE; enumerator = message->create_attribute_enumerator(message); @@ -150,6 +151,9 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message old_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, value.ike_sa_id); break; + case HA_REMOTE_ADDR: + other = value.host->clone(value.host); + break; case HA_IKE_VERSION: version = value.u8; break; @@ -252,6 +256,11 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message charon->ike_sa_manager, old_sa); old_sa = NULL; } + if (other) + { + ike_sa->set_other_host(ike_sa, other); + other = NULL; + } ike_sa->set_state(ike_sa, IKE_CONNECTING); ike_sa->set_proposal(ike_sa, proposal); this->cache->cache(this->cache, ike_sa, message); @@ -270,6 +279,7 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message { charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa); } + DESTROY_IF(other); DESTROY_IF(message); } @@ -637,7 +647,7 @@ static void process_child_add(private_ha_dispatcher_t *this, u_int32_t inbound_spi = 0, outbound_spi = 0; u_int16_t inbound_cpi = 0, outbound_cpi = 0; u_int8_t mode = MODE_TUNNEL, ipcomp = 0; - u_int16_t encr = ENCR_UNDEFINED, integ = AUTH_UNDEFINED, len = 0; + u_int16_t encr = 0, integ = 0, len = 0; u_int16_t esn = NO_EXT_SEQ_NUMBERS; u_int seg_i, seg_o; chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty; diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index 6b4b53c9c..7492dd06e 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -138,6 +138,7 @@ METHOD(listener_t, ike_keys, bool, m->add_attribute(m, HA_PSK, shared->get_key(shared)); } } + m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa)); this->socket->push(this->socket, m); this->cache->cache(this->cache, ike_sa, m); diff --git a/src/libcharon/plugins/ha/ha_kernel.c b/src/libcharon/plugins/ha/ha_kernel.c index eed89e0bf..bd43dc351 100644 --- a/src/libcharon/plugins/ha/ha_kernel.c +++ b/src/libcharon/plugins/ha/ha_kernel.c @@ -36,6 +36,8 @@ typedef enum { JHASH_LOOKUP2, /* new variant, http://burtleburtle.net/bob/c/lookup3.c, since 2.6.37 */ JHASH_LOOKUP3, + /* variant with different init values, since 4.1 */ + JHASH_LOOKUP3_1, } jhash_version_t; typedef struct private_ha_kernel_t private_ha_kernel_t; @@ -88,8 +90,15 @@ static jhash_version_t get_jhash_version() } /* FALL */ case 2: - DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash", a, b); - return JHASH_LOOKUP3; + if (a < 4 || (a == 4 && b == 0)) + { + DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash", + a, b); + return JHASH_LOOKUP3; + } + DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash with " + "updated init values", a, b); + return JHASH_LOOKUP3_1; default: break; } @@ -126,6 +135,14 @@ static u_int32_t jhash(jhash_version_t version, u_int32_t a, u_int32_t b) b -= c; b -= a; b ^= (a << 10); c -= a; c -= b; c ^= (b >> 15); break; + case JHASH_LOOKUP3_1: + /* changed with 4.1: # of 32-bit words shifted by 2 and c is + * initialized. we only use the two word variant with SPIs, so it's + * unlikely that b is 0 in that case */ + c += ((b ? 2 : 1) << 2) + 0xdeadbeef; + a += ((b ? 2 : 1) << 2); + b += ((b ? 2 : 1) << 2); + /* FALL */ case JHASH_LOOKUP3: a += 0xdeadbeef; b += 0xdeadbeef; diff --git a/src/libcharon/plugins/load_tester/load_tester.c b/src/libcharon/plugins/load_tester/load_tester.c index b7b971ee8..f5a998ecc 100644 --- a/src/libcharon/plugins/load_tester/load_tester.c +++ b/src/libcharon/plugins/load_tester/load_tester.c @@ -21,6 +21,7 @@ #include <stddef.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <errno.h> /** diff --git a/src/libcharon/plugins/load_tester/load_tester_control.c b/src/libcharon/plugins/load_tester/load_tester_control.c index 5f089f5db..24076d443 100644 --- a/src/libcharon/plugins/load_tester/load_tester_control.c +++ b/src/libcharon/plugins/load_tester/load_tester_control.c @@ -239,7 +239,7 @@ static bool on_accept(private_load_tester_control_t *this, stream_t *io) switch (charon->controller->initiate(charon->controller, peer_cfg, child_cfg->get_ref(child_cfg), - (void*)initiate_cb, listener, 0)) + (void*)initiate_cb, listener, 0, FALSE)) { case NEED_MORE: /* Callback returns FALSE once it got track of this IKE_SA. diff --git a/src/libcharon/plugins/load_tester/load_tester_plugin.c b/src/libcharon/plugins/load_tester/load_tester_plugin.c index e684f22ce..c7380b974 100644 --- a/src/libcharon/plugins/load_tester/load_tester_plugin.c +++ b/src/libcharon/plugins/load_tester/load_tester_plugin.c @@ -152,7 +152,7 @@ static job_requeue_t do_load_test(private_load_tester_plugin_t *this) charon->controller->initiate(charon->controller, peer_cfg, child_cfg->get_ref(child_cfg), - NULL, NULL, 0); + NULL, NULL, 0, FALSE); if (s) { sleep(s); diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 1fb57b928..25b138387 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -314,7 +314,7 @@ static job_requeue_t initiate_config(peer_cfg_t *peer_cfg) peer_cfg->get_ref(peer_cfg); enumerator->destroy(enumerator); charon->controller->initiate(charon->controller, - peer_cfg, child_cfg, NULL, NULL, 0); + peer_cfg, child_cfg, NULL, NULL, 0, FALSE); } else { diff --git a/src/libcharon/plugins/osx_attr/osx_attr_handler.c b/src/libcharon/plugins/osx_attr/osx_attr_handler.c index d974b57ce..6baf76d35 100644 --- a/src/libcharon/plugins/osx_attr/osx_attr_handler.c +++ b/src/libcharon/plugins/osx_attr/osx_attr_handler.c @@ -31,6 +31,16 @@ struct private_osx_attr_handler_t { * Public interface */ osx_attr_handler_t public; + + /** + * Backup of original DNS servers, before we mess with it + */ + CFMutableArrayRef original; + + /** + * Append DNS servers to existing entries, instead of replacing + */ + bool append; }; /** @@ -110,7 +120,8 @@ static CFMutableArrayRef get_array_from_dict(CFDictionaryRef dict, /** * Add/Remove a DNS server to the configuration */ -static bool manage_dns(int family, chunk_t data, bool add) +static bool manage_dns(private_osx_attr_handler_t *this, + int family, chunk_t data, bool add) { SCDynamicStoreRef store; CFStringRef path, dns; @@ -138,6 +149,11 @@ static bool manage_dns(int family, chunk_t data, bool add) dns = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); if (add) { + if (!this->append && !this->original) + { /* backup orignal config, start with empty set */ + this->original = arr; + arr = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } DBG1(DBG_CFG, "installing %s as DNS server", buf); CFArrayInsertValueAtIndex(arr, 0, dns); } @@ -150,6 +166,12 @@ static bool manage_dns(int family, chunk_t data, bool add) DBG1(DBG_CFG, "removing %s from DNS servers (%d)", buf, i); CFArrayRemoveValueAtIndex(arr, i); } + if (!this->append && this->original && CFArrayGetCount(arr) == 0) + { /* restore original config */ + CFRelease(arr); + arr = this->original; + this->original = NULL; + } } CFRelease(dns); CFDictionarySetValue(dict, CFSTR("ServerAddresses"), arr); @@ -175,7 +197,7 @@ METHOD(attribute_handler_t, handle, bool, switch (type) { case INTERNAL_IP4_DNS: - return manage_dns(AF_INET, data, TRUE); + return manage_dns(this, AF_INET, data, TRUE); default: return FALSE; } @@ -188,7 +210,7 @@ METHOD(attribute_handler_t, release, void, switch (type) { case INTERNAL_IP4_DNS: - manage_dns(AF_INET, data, FALSE); + manage_dns(this, AF_INET, data, FALSE); break; default: break; @@ -240,6 +262,8 @@ osx_attr_handler_t *osx_attr_handler_create() }, .destroy = _destroy, }, + .append = lib->settings->get_bool(lib->settings, + "%s.plugins.osx-attr.append", TRUE, lib->ns), ); return &this->public; diff --git a/src/libcharon/plugins/smp/smp.c b/src/libcharon/plugins/smp/smp.c index 04bf382ed..2aa061fd2 100644 --- a/src/libcharon/plugins/smp/smp.c +++ b/src/libcharon/plugins/smp/smp.c @@ -488,7 +488,7 @@ static void request_control_initiate(xmlTextReaderPtr reader, { status = charon->controller->initiate(charon->controller, peer, child, (controller_cb_t)xml_callback, - writer, 0); + writer, 0, FALSE); } else { diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index c47c7c0f8..ce24d180a 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -324,6 +324,14 @@ static peer_cfg_t *get_peer_cfg_by_id(private_sql_config_t *this, int id) } /** + * Check if the two IDs match (the first one is optional) + */ +static inline bool id_matches(identification_t *id, identification_t *sql_id) +{ + return !id || id->matches(id, sql_id) || sql_id->matches(sql_id, id); +} + +/** * Build a peer config from an SQL query */ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, @@ -352,8 +360,7 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, local_id = identification_create_from_encoding(l_type, l_data); remote_id = identification_create_from_encoding(r_type, r_data); - if ((me && !me->matches(me, local_id)) || - (other && !other->matches(other, remote_id))) + if (!id_matches(me, local_id) || !id_matches(other, remote_id)) { local_id->destroy(local_id); remote_id->destroy(remote_id); diff --git a/src/libcharon/plugins/sql/sql_logger.c b/src/libcharon/plugins/sql/sql_logger.c index 9a7a6e0ff..0fa06eac5 100644 --- a/src/libcharon/plugins/sql/sql_logger.c +++ b/src/libcharon/plugins/sql/sql_logger.c @@ -120,6 +120,7 @@ METHOD(logger_t, get_level, level_t, METHOD(sql_logger_t, destroy, void, private_sql_logger_t *this) { + this->recursive->destroy(this->recursive); free(this); } diff --git a/src/libcharon/plugins/stroke/stroke_ca.c b/src/libcharon/plugins/stroke/stroke_ca.c index b470b81c6..13ed41e0e 100644 --- a/src/libcharon/plugins/stroke/stroke_ca.c +++ b/src/libcharon/plugins/stroke/stroke_ca.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -24,6 +24,13 @@ #include <daemon.h> typedef struct private_stroke_ca_t private_stroke_ca_t; +typedef struct ca_section_t ca_section_t; +typedef struct ca_cert_t ca_cert_t; + +/** + * Provided by stroke_cred.c + */ +certificate_t *stroke_load_ca_cert(char *filename); /** * private data of stroke_ca @@ -41,17 +48,16 @@ struct private_stroke_ca_t { rwlock_t *lock; /** - * list of starters CA sections and its certificates (ca_section_t) + * list of CA sections and their certificates (ca_section_t) */ linked_list_t *sections; /** - * stroke credentials, stores our CA certificates + * list of all loaded CA certificates (ca_cert_t) */ - stroke_cred_t *cred; + linked_list_t *certs; }; -typedef struct ca_section_t ca_section_t; /** * loaded ipsec.conf CA sections @@ -64,7 +70,12 @@ struct ca_section_t { char *name; /** - * reference to cert in trusted_credential_t + * path/name of the certificate + */ + char *path; + + /** + * reference to cert */ certificate_t *cert; @@ -90,16 +101,37 @@ struct ca_section_t { }; /** + * loaded CA certificate + */ +struct ca_cert_t { + + /** + * reference to cert + */ + certificate_t *cert; + + /** + * The number of CA sections referring to this certificate + */ + u_int count; + + /** + * TRUE if this certificate was automatically loaded + */ + bool automatic; +}; + +/** * create a new CA section */ -static ca_section_t *ca_section_create(char *name, certificate_t *cert) +static ca_section_t *ca_section_create(char *name, char *path) { ca_section_t *ca = malloc_thing(ca_section_t); ca->name = strdup(name); + ca->path = strdup(path); ca->crl = linked_list_create(); ca->ocsp = linked_list_create(); - ca->cert = cert; ca->hashes = linked_list_create(); ca->certuribase = NULL; return ca; @@ -115,11 +147,21 @@ static void ca_section_destroy(ca_section_t *this) this->hashes->destroy_offset(this->hashes, offsetof(identification_t, destroy)); this->cert->destroy(this->cert); free(this->certuribase); + free(this->path); free(this->name); free(this); } /** + * Destroy a ca cert entry + */ +static void ca_cert_destroy(ca_cert_t *this) +{ + this->cert->destroy(this->cert); + free(this); +} + +/** * Data for the certificate enumerator */ typedef struct { @@ -141,7 +183,7 @@ static void cert_data_destroy(cert_data_t *data) /** * filter function for certs enumerator */ -static bool certs_filter(cert_data_t *data, ca_section_t **in, +static bool certs_filter(cert_data_t *data, ca_cert_t **in, certificate_t **out) { public_key_t *public; @@ -192,7 +234,7 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, ); this->lock->read_lock(this->lock); - enumerator = this->sections->create_enumerator(this->sections); + enumerator = this->certs->create_enumerator(this->certs); return enumerator_create_filter(enumerator, (void*)certs_filter, data, (void*)cert_data_destroy); } @@ -312,6 +354,81 @@ METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*, data, (void*)cdp_data_destroy); } +/** + * Compare the given certificate to the ca_cert_t items in the list + */ +static bool match_cert(ca_cert_t *item, certificate_t *cert) +{ + return cert->equals(cert, item->cert); +} + +/** + * Match automatically added certificates and remove/destroy them if they are + * not referenced by CA sections. + */ +static bool remove_auto_certs(ca_cert_t *item, void *not_used) +{ + if (item->automatic) + { + item->automatic = FALSE; + if (!item->count) + { + ca_cert_destroy(item); + return TRUE; + } + } + return FALSE; +} + +/** + * Find the given certificate that was referenced by a section and remove it + * unless it was also loaded automatically or is used by other CA sections. + */ +static bool remove_cert(ca_cert_t *item, certificate_t *cert) +{ + if (item->count && cert->equals(cert, item->cert)) + { + if (--item->count == 0 && !item->automatic) + { + ca_cert_destroy(item); + return TRUE; + } + } + return FALSE; +} + +/** + * Adds a certificate to the certificate store + */ +static certificate_t *add_cert_internal(private_stroke_ca_t *this, + certificate_t *cert, bool automatic) +{ + ca_cert_t *found; + + if (this->certs->find_first(this->certs, (linked_list_match_t)match_cert, + (void**)&found, cert) == SUCCESS) + { + cert->destroy(cert); + cert = found->cert->get_ref(found->cert); + } + else + { + INIT(found, + .cert = cert->get_ref(cert) + ); + this->certs->insert_first(this->certs, found); + } + if (automatic) + { + found->automatic = TRUE; + } + else + { + found->count++; + } + return cert; +} + METHOD(stroke_ca_t, add, void, private_stroke_ca_t *this, stroke_msg_t *msg) { @@ -323,10 +440,10 @@ METHOD(stroke_ca_t, add, void, DBG1(DBG_CFG, "missing cacert parameter"); return; } - cert = this->cred->load_ca(this->cred, msg->add_ca.cacert); + cert = stroke_load_ca_cert(msg->add_ca.cacert); if (cert) { - ca = ca_section_create(msg->add_ca.name, cert); + ca = ca_section_create(msg->add_ca.name, msg->add_ca.cacert); if (msg->add_ca.crluri) { ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri)); @@ -348,6 +465,7 @@ METHOD(stroke_ca_t, add, void, ca->certuribase = strdup(msg->add_ca.certuribase); } this->lock->write_lock(this->lock); + ca->cert = add_cert_internal(this, cert, FALSE); this->sections->insert_last(this->sections, ca); this->lock->unlock(this->lock); DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name); @@ -372,8 +490,12 @@ METHOD(stroke_ca_t, del, void, ca = NULL; } enumerator->destroy(enumerator); + if (ca) + { + this->certs->remove(this->certs, ca->cert, (void*)remove_cert); + } this->lock->unlock(this->lock); - if (ca == NULL) + if (!ca) { DBG1(DBG_CFG, "no ca named '%s' found\n", msg->del_ca.name); return; @@ -383,6 +505,88 @@ METHOD(stroke_ca_t, del, void, lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); } +METHOD(stroke_ca_t, get_cert_ref, certificate_t*, + private_stroke_ca_t *this, certificate_t *cert) +{ + ca_cert_t *found; + + this->lock->read_lock(this->lock); + if (this->certs->find_first(this->certs, (linked_list_match_t)match_cert, + (void**)&found, cert) == SUCCESS) + { + cert->destroy(cert); + cert = found->cert->get_ref(found->cert); + } + this->lock->unlock(this->lock); + return cert; +} + +METHOD(stroke_ca_t, reload_certs, void, + private_stroke_ca_t *this) +{ + enumerator_t *enumerator; + certificate_t *cert; + ca_section_t *ca; + certificate_type_t type = CERT_X509; + + /* holding the write lock while loading/parsing certificates is not optimal, + * however, there usually are not that many ca sections configured */ + this->lock->write_lock(this->lock); + if (this->sections->get_count(this->sections)) + { + DBG1(DBG_CFG, "rereading ca certificates in ca sections"); + } + enumerator = this->sections->create_enumerator(this->sections); + while (enumerator->enumerate(enumerator, &ca)) + { + cert = stroke_load_ca_cert(ca->path); + if (cert) + { + if (cert->equals(cert, ca->cert)) + { + cert->destroy(cert); + } + else + { + this->certs->remove(this->certs, ca->cert, (void*)remove_cert); + ca->cert->destroy(ca->cert); + ca->cert = add_cert_internal(this, cert, FALSE); + } + } + else + { + DBG1(DBG_CFG, "failed to reload certificate '%s', removing ca '%s'", + ca->path, ca->name); + this->sections->remove_at(this->sections, enumerator); + this->certs->remove(this->certs, ca->cert, (void*)remove_cert); + ca_section_destroy(ca); + type = CERT_ANY; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + lib->credmgr->flush_cache(lib->credmgr, type); +} + +METHOD(stroke_ca_t, replace_certs, void, + private_stroke_ca_t *this, mem_cred_t *certs) +{ + enumerator_t *enumerator; + certificate_t *cert; + + enumerator = certs->set.create_cert_enumerator(&certs->set, CERT_X509, + KEY_ANY, NULL, TRUE); + this->lock->write_lock(this->lock); + this->certs->remove(this->certs, NULL, (void*)remove_auto_certs); + while (enumerator->enumerate(enumerator, &cert)) + { + cert = add_cert_internal(this, cert->get_ref(cert), TRUE); + cert->destroy(cert); + } + this->lock->unlock(this->lock); + enumerator->destroy(enumerator); + lib->credmgr->flush_cache(lib->credmgr, CERT_X509); +} /** * list crl or ocsp URIs */ @@ -501,6 +705,7 @@ METHOD(stroke_ca_t, destroy, void, private_stroke_ca_t *this) { this->sections->destroy_function(this->sections, (void*)ca_section_destroy); + this->certs->destroy_function(this->certs, (void*)ca_cert_destroy); this->lock->destroy(this->lock); free(this); } @@ -508,7 +713,7 @@ METHOD(stroke_ca_t, destroy, void, /* * see header file */ -stroke_ca_t *stroke_ca_create(stroke_cred_t *cred) +stroke_ca_t *stroke_ca_create() { private_stroke_ca_t *this; @@ -524,12 +729,15 @@ stroke_ca_t *stroke_ca_create(stroke_cred_t *cred) .add = _add, .del = _del, .list = _list, + .get_cert_ref = _get_cert_ref, + .reload_certs = _reload_certs, + .replace_certs = _replace_certs, .check_for_hash_and_url = _check_for_hash_and_url, .destroy = _destroy, }, .sections = linked_list_create(), + .certs = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), - .cred = cred, ); return &this->public; diff --git a/src/libcharon/plugins/stroke/stroke_ca.h b/src/libcharon/plugins/stroke/stroke_ca.h index 21af912ea..2740006e2 100644 --- a/src/libcharon/plugins/stroke/stroke_ca.h +++ b/src/libcharon/plugins/stroke/stroke_ca.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -23,8 +23,7 @@ #define STROKE_CA_H_ #include <stroke_msg.h> - -#include "stroke_cred.h" +#include <credentials/sets/mem_cred.h> typedef struct stroke_ca_t stroke_ca_t; @@ -67,6 +66,29 @@ struct stroke_ca_t { void (*check_for_hash_and_url)(stroke_ca_t *this, certificate_t* cert); /** + * Get a reference to a CA certificate if it is already stored, + * otherwise returns the same certificate. + * + * @param cert certificate to check + * @return reference to stored CA certifiate, or original + */ + certificate_t *(*get_cert_ref)(stroke_ca_t *this, certificate_t *cert); + + /** + * Reload CA certificates referenced in CA sections. Flushes the certificate + * cache. + */ + void (*reload_certs)(stroke_ca_t *this); + + /** + * Replace automatically loaded CA certificates. Flushes the certificate + * cache. + * + * @param certs credential set to take certificates from (not modified) + */ + void (*replace_certs)(stroke_ca_t *this, mem_cred_t *certs); + + /** * Destroy a stroke_ca instance. */ void (*destroy)(stroke_ca_t *this); @@ -75,6 +97,6 @@ struct stroke_ca_t { /** * Create a stroke_ca instance. */ -stroke_ca_t *stroke_ca_create(stroke_cred_t *cred); +stroke_ca_t *stroke_ca_create(); #endif /** STROKE_CA_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 55ec7cdc9..f71719458 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -184,19 +184,16 @@ static void add_proposals(private_stroke_config_t *this, char *string, } /** - * Build an IKE config from a stroke message + * Check if any addresses in the given string are local */ -static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg) +static bool is_local(char *address, bool any_allowed) { enumerator_t *enumerator; - stroke_end_t tmp_end; - ike_cfg_t *ike_cfg; host_t *host; - u_int16_t ikeport; - char me[256], other[256], *token; - bool swapped = FALSE;; + char *token; + bool found = FALSE; - enumerator = enumerator_create_token(msg->add_conn.other.address, ",", " "); + enumerator = enumerator_create_token(address, ",", " "); while (enumerator->enumerate(enumerator, &token)) { if (!strchr(token, '/')) @@ -207,40 +204,60 @@ static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg if (hydra->kernel_interface->get_interface( hydra->kernel_interface, host, NULL)) { - DBG2(DBG_CFG, "left is other host, swapping ends"); - tmp_end = msg->add_conn.me; - msg->add_conn.me = msg->add_conn.other; - msg->add_conn.other = tmp_end; - swapped = TRUE; + found = TRUE; + } + else if (any_allowed && host->is_anyaddr(host)) + { + found = TRUE; } host->destroy(host); + if (found) + { + break; + } } } } enumerator->destroy(enumerator); + return found; +} - if (!swapped) +/** + * Swap ends if indicated by left|right + */ +static void swap_ends(stroke_msg_t *msg) +{ + if (!lib->settings->get_bool(lib->settings, "%s.plugins.stroke.allow_swap", + TRUE, lib->ns)) { - enumerator = enumerator_create_token(msg->add_conn.me.address, ",", " "); - while (enumerator->enumerate(enumerator, &token)) - { - if (!strchr(token, '/')) - { - host = host_create_from_dns(token, 0, 0); - if (host) - { - if (!hydra->kernel_interface->get_interface( - hydra->kernel_interface, host, NULL)) - { - DBG1(DBG_CFG, "left nor right host is our side, " - "assuming left=local"); - } - host->destroy(host); - } - } - } - enumerator->destroy(enumerator); + return; + } + + if (is_local(msg->add_conn.other.address, FALSE)) + { + stroke_end_t tmp_end; + + DBG2(DBG_CFG, "left is other host, swapping ends"); + tmp_end = msg->add_conn.me; + msg->add_conn.me = msg->add_conn.other; + msg->add_conn.other = tmp_end; + } + else if (!is_local(msg->add_conn.me.address, TRUE)) + { + DBG1(DBG_CFG, "left nor right host is our side, assuming left=local"); } +} + +/** + * Build an IKE config from a stroke message + */ +static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg) +{ + ike_cfg_t *ike_cfg; + u_int16_t ikeport; + char me[256], other[256]; + + swap_ends(msg); if (msg->add_conn.me.allow_any) { diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index 0084fbf93..0125d17c6 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -109,7 +109,7 @@ static void charon_initiate(private_stroke_control_t *this, peer_cfg_t *peer_cfg if (msg->output_verbosity < 0) { charon->controller->initiate(charon->controller, peer_cfg, child_cfg, - NULL, NULL, 0); + NULL, NULL, 0, FALSE); } else { @@ -118,7 +118,7 @@ static void charon_initiate(private_stroke_control_t *this, peer_cfg_t *peer_cfg status = charon->controller->initiate(charon->controller, peer_cfg, child_cfg, (controller_cb_t)stroke_log, - &info, this->timeout); + &info, this->timeout, FALSE); switch (status) { case SUCCESS: diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index 5e423f1de..42928882a 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2013 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -75,11 +75,6 @@ struct private_stroke_cred_t { mem_cred_t *creds; /** - * CA certificates - */ - mem_cred_t *cacerts; - - /** * Attribute Authority certificates */ mem_cred_t *aacerts; @@ -94,6 +89,11 @@ struct private_stroke_cred_t { * cache CRLs to disk? */ bool cachecrl; + + /** + * CA certificate store + */ + stroke_ca_t *ca; }; /** Length of smartcard specifier parts (module, keyid) */ @@ -182,70 +182,6 @@ static certificate_t *load_from_smartcard(smartcard_format_t format, return cred; } -METHOD(stroke_cred_t, load_ca, certificate_t*, - private_stroke_cred_t *this, char *filename) -{ - certificate_t *cert = NULL; - char path[PATH_MAX]; - - if (strpfx(filename, "%smartcard")) - { - smartcard_format_t format; - char module[SC_PART_LEN], keyid[SC_PART_LEN]; - u_int slot; - - format = parse_smartcard(filename, &slot, module, keyid); - if (format != SC_FORMAT_INVALID) - { - cert = (certificate_t*)load_from_smartcard(format, - slot, module, keyid, CRED_CERTIFICATE, CERT_X509); - } - } - else - { - if (*filename == '/') - { - snprintf(path, sizeof(path), "%s", filename); - } - else - { - snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename); - } - - if (this->force_ca_cert) - { /* we treat this certificate as a CA certificate even if it has no - * CA basic constraint */ - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, path, BUILD_X509_FLAG, X509_CA, - BUILD_END); - } - else - { - cert = lib->creds->create(lib->creds, - CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, path, - BUILD_END); - } - } - if (cert) - { - x509_t *x509 = (x509_t*)cert; - - if (!(x509->get_flags(x509) & X509_CA)) - { - DBG1(DBG_CFG, " ca certificate \"%Y\" misses ca basic constraint, " - "discarded", cert->get_subject(cert)); - cert->destroy(cert); - return NULL; - } - DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", - cert->get_subject(cert), filename); - return this->creds->get_cert_ref(this->creds, cert); - } - return NULL; -} - METHOD(stroke_cred_t, load_peer, certificate_t*, private_stroke_cred_t *this, char *filename) { @@ -384,22 +320,52 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*, } /** - * Load a CA certificate from disk + * Load a CA certificate, optionally force it to be one */ -static void load_x509_ca(private_stroke_cred_t *this, char *file) +static certificate_t *load_ca_cert(char *filename, bool force_ca_cert) { - certificate_t *cert; + certificate_t *cert = NULL; + char path[PATH_MAX]; + + if (strpfx(filename, "%smartcard")) + { + smartcard_format_t format; + char module[SC_PART_LEN], keyid[SC_PART_LEN]; + u_int slot; - if (this->force_ca_cert) - { /* treat certificate as CA cert even it has no CA basic constraint */ - cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, file, - BUILD_X509_FLAG, X509_CA, BUILD_END); + format = parse_smartcard(filename, &slot, module, keyid); + if (format != SC_FORMAT_INVALID) + { + cert = (certificate_t*)load_from_smartcard(format, + slot, module, keyid, CRED_CERTIFICATE, CERT_X509); + } } else { - cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, - BUILD_FROM_FILE, file, BUILD_END); + if (*filename == '/') + { + snprintf(path, sizeof(path), "%s", filename); + } + else + { + snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename); + } + + if (force_ca_cert) + { /* we treat this certificate as a CA certificate even if it has no + * CA basic constraint */ + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, path, BUILD_X509_FLAG, X509_CA, + BUILD_END); + } + else + { + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, path, + BUILD_END); + } } if (cert) { @@ -410,13 +376,41 @@ static void load_x509_ca(private_stroke_cred_t *this, char *file) DBG1(DBG_CFG, " ca certificate \"%Y\" lacks ca basic constraint, " "discarded", cert->get_subject(cert)); cert->destroy(cert); + return NULL; } - else - { - DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", - cert->get_subject(cert), file); - this->cacerts->add_cert(this->cacerts, TRUE, cert); - } + DBG1(DBG_CFG, " loaded ca certificate \"%Y\" from '%s'", + cert->get_subject(cert), filename); + return cert; + } + return NULL; +} + +/** + * Used by stroke_ca.c + */ +certificate_t *stroke_load_ca_cert(char *filename) +{ + bool force_ca_cert; + + force_ca_cert = lib->settings->get_bool(lib->settings, + "%s.plugins.stroke.ignore_missing_ca_basic_constraint", + FALSE, lib->ns); + return load_ca_cert(filename, force_ca_cert); +} + +/** + * Load a CA certificate from disk + */ +static void load_x509_ca(private_stroke_cred_t *this, char *file, + mem_cred_t *creds) +{ + certificate_t *cert; + + cert = load_ca_cert(file, this->force_ca_cert); + if (cert) + { + cert = this->ca->get_cert_ref(this->ca, cert); + creds->add_cert(creds, TRUE, cert); } else { @@ -427,7 +421,8 @@ static void load_x509_ca(private_stroke_cred_t *this, char *file) /** * Load AA certificate with flags from disk */ -static void load_x509_aa(private_stroke_cred_t *this, char *file) +static void load_x509_aa(private_stroke_cred_t *this,char *file, + mem_cred_t *creds) { certificate_t *cert; @@ -438,7 +433,7 @@ static void load_x509_aa(private_stroke_cred_t *this, char *file) { DBG1(DBG_CFG, " loaded AA certificate \"%Y\" from '%s'", cert->get_subject(cert), file); - this->aacerts->add_cert(this->aacerts, TRUE, cert); + creds->add_cert(creds, TRUE, cert); } else { @@ -449,7 +444,8 @@ static void load_x509_aa(private_stroke_cred_t *this, char *file) /** * Load a certificate with flags from disk */ -static void load_x509(private_stroke_cred_t *this, char *file, x509_flag_t flag) +static void load_x509(private_stroke_cred_t *this, char *file, x509_flag_t flag, + mem_cred_t *creds) { certificate_t *cert; @@ -461,7 +457,7 @@ static void load_x509(private_stroke_cred_t *this, char *file, x509_flag_t flag) { DBG1(DBG_CFG, " loaded certificate \"%Y\" from '%s'", cert->get_subject(cert), file); - this->creds->add_cert(this->creds, TRUE, cert); + creds->add_cert(creds, TRUE, cert); } else { @@ -472,7 +468,8 @@ static void load_x509(private_stroke_cred_t *this, char *file, x509_flag_t flag) /** * Load a CRL from a file */ -static void load_x509_crl(private_stroke_cred_t *this, char *file) +static void load_x509_crl(private_stroke_cred_t *this, char *file, + mem_cred_t *creds) { certificate_t *cert; @@ -480,8 +477,8 @@ static void load_x509_crl(private_stroke_cred_t *this, char *file) BUILD_FROM_FILE, file, BUILD_END); if (cert) { - this->creds->add_crl(this->creds, (crl_t*)cert); DBG1(DBG_CFG, " loaded crl from '%s'", file); + creds->add_crl(creds, (crl_t*)cert); } else { @@ -492,7 +489,8 @@ static void load_x509_crl(private_stroke_cred_t *this, char *file) /** * Load an attribute certificate from a file */ -static void load_x509_ac(private_stroke_cred_t *this, char *file) +static void load_x509_ac(private_stroke_cred_t *this, char *file, + mem_cred_t *creds) { certificate_t *cert; @@ -501,7 +499,7 @@ static void load_x509_ac(private_stroke_cred_t *this, char *file) if (cert) { DBG1(DBG_CFG, " loaded attribute certificate from '%s'", file); - this->creds->add_cert(this->creds, FALSE, cert); + creds->add_cert(creds, FALSE, cert); } else { @@ -513,7 +511,8 @@ static void load_x509_ac(private_stroke_cred_t *this, char *file) * load trusted certificates from a directory */ static void load_certdir(private_stroke_cred_t *this, char *path, - certificate_type_t type, x509_flag_t flag) + certificate_type_t type, x509_flag_t flag, + mem_cred_t *creds) { enumerator_t *enumerator; struct stat st; @@ -534,22 +533,22 @@ static void load_certdir(private_stroke_cred_t *this, char *path, case CERT_X509: if (flag & X509_CA) { - load_x509_ca(this, file); + load_x509_ca(this, file, creds); } else if (flag & X509_AA) { - load_x509_aa(this, file); + load_x509_aa(this, file, creds); } else { - load_x509(this, file, flag); + load_x509(this, file, flag, creds); } break; case CERT_X509_CRL: - load_x509_crl(this, file); + load_x509_crl(this, file, creds); break; case CERT_X509_AC: - load_x509_ac(this, file); + load_x509_ac(this, file, creds); break; default: break; @@ -1348,30 +1347,38 @@ static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets, */ static void load_certs(private_stroke_cred_t *this) { + mem_cred_t *creds; + DBG1(DBG_CFG, "loading ca certificates from '%s'", CA_CERTIFICATE_DIR); - load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA); + creds = mem_cred_create(); + load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA, creds); + this->ca->replace_certs(this->ca, creds); + creds->destroy(creds); DBG1(DBG_CFG, "loading aa certificates from '%s'", AA_CERTIFICATE_DIR); - load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA); + load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA, this->aacerts); DBG1(DBG_CFG, "loading ocsp signer certificates from '%s'", OCSP_CERTIFICATE_DIR); - load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509, X509_OCSP_SIGNER); + load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509, X509_OCSP_SIGNER, + this->creds); DBG1(DBG_CFG, "loading attribute certificates from '%s'", ATTR_CERTIFICATE_DIR); - load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0); + load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0, this->creds); DBG1(DBG_CFG, "loading crls from '%s'", CRL_DIR); - load_certdir(this, CRL_DIR, CERT_X509_CRL, 0); + load_certdir(this, CRL_DIR, CERT_X509_CRL, 0, this->creds); } METHOD(stroke_cred_t, reread, void, private_stroke_cred_t *this, stroke_msg_t *msg, FILE *prompt) { + mem_cred_t *creds; + if (msg->reread.flags & REREAD_SECRETS) { DBG1(DBG_CFG, "rereading secrets"); @@ -1379,38 +1386,44 @@ METHOD(stroke_cred_t, reread, void, } if (msg->reread.flags & REREAD_CACERTS) { + /* first reload certificates in ca sections, so we can refer to them */ + this->ca->reload_certs(this->ca); + DBG1(DBG_CFG, "rereading ca certificates from '%s'", CA_CERTIFICATE_DIR); - this->cacerts->clear(this->cacerts); + creds = mem_cred_create(); + load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA, creds); + this->ca->replace_certs(this->ca, creds); + creds->destroy(creds); + } + if (msg->reread.flags & REREAD_AACERTS) + { + DBG1(DBG_CFG, "rereading aa certificates from '%s'", + AA_CERTIFICATE_DIR); + creds = mem_cred_create(); + load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA, creds); + this->aacerts->replace_certs(this->aacerts, creds, FALSE); + creds->destroy(creds); lib->credmgr->flush_cache(lib->credmgr, CERT_X509); - load_certdir(this, CA_CERTIFICATE_DIR, CERT_X509, X509_CA); } if (msg->reread.flags & REREAD_OCSPCERTS) { DBG1(DBG_CFG, "rereading ocsp signer certificates from '%s'", OCSP_CERTIFICATE_DIR); load_certdir(this, OCSP_CERTIFICATE_DIR, CERT_X509, - X509_OCSP_SIGNER); - } - if (msg->reread.flags & REREAD_AACERTS) - { - DBG1(DBG_CFG, "rereading aa certificates from '%s'", - AA_CERTIFICATE_DIR); - this->aacerts->clear(this->aacerts); - lib->credmgr->flush_cache(lib->credmgr, CERT_X509); - load_certdir(this, AA_CERTIFICATE_DIR, CERT_X509, X509_AA); + X509_OCSP_SIGNER, this->creds); } if (msg->reread.flags & REREAD_ACERTS) { DBG1(DBG_CFG, "rereading attribute certificates from '%s'", ATTR_CERTIFICATE_DIR); - load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0); + load_certdir(this, ATTR_CERTIFICATE_DIR, CERT_X509_AC, 0, this->creds); } if (msg->reread.flags & REREAD_CRLS) { DBG1(DBG_CFG, "rereading crls from '%s'", CRL_DIR); - load_certdir(this, CRL_DIR, CERT_X509_CRL, 0); + load_certdir(this, CRL_DIR, CERT_X509_CRL, 0, this->creds); } } @@ -1424,10 +1437,8 @@ METHOD(stroke_cred_t, destroy, void, private_stroke_cred_t *this) { lib->credmgr->remove_set(lib->credmgr, &this->aacerts->set); - lib->credmgr->remove_set(lib->credmgr, &this->cacerts->set); lib->credmgr->remove_set(lib->credmgr, &this->creds->set); this->aacerts->destroy(this->aacerts); - this->cacerts->destroy(this->cacerts); this->creds->destroy(this->creds); free(this); } @@ -1435,7 +1446,7 @@ METHOD(stroke_cred_t, destroy, void, /* * see header file */ -stroke_cred_t *stroke_cred_create() +stroke_cred_t *stroke_cred_create(stroke_ca_t *ca) { private_stroke_cred_t *this; @@ -1449,7 +1460,6 @@ stroke_cred_t *stroke_cred_create() .cache_cert = (void*)_cache_cert, }, .reread = _reread, - .load_ca = _load_ca, .load_peer = _load_peer, .load_pubkey = _load_pubkey, .add_shared = _add_shared, @@ -1460,12 +1470,11 @@ stroke_cred_t *stroke_cred_create() "%s.plugins.stroke.secrets_file", SECRETS_FILE, lib->ns), .creds = mem_cred_create(), - .cacerts = mem_cred_create(), .aacerts = mem_cred_create(), + .ca = ca, ); lib->credmgr->add_set(lib->credmgr, &this->creds->set); - lib->credmgr->add_set(lib->credmgr, &this->cacerts->set); lib->credmgr->add_set(lib->credmgr, &this->aacerts->set); this->force_ca_cert = lib->settings->get_bool(lib->settings, diff --git a/src/libcharon/plugins/stroke/stroke_cred.h b/src/libcharon/plugins/stroke/stroke_cred.h index 9434629ef..33a0e3531 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.h +++ b/src/libcharon/plugins/stroke/stroke_cred.h @@ -29,6 +29,8 @@ #include <credentials/certificates/certificate.h> #include <collections/linked_list.h> +#include "stroke_ca.h" + typedef struct stroke_cred_t stroke_cred_t; /** @@ -50,17 +52,6 @@ struct stroke_cred_t { void (*reread)(stroke_cred_t *this, stroke_msg_t *msg, FILE *prompt); /** - * Load a CA certificate. - * - * This method does not add the loaded CA certificate to the internal - * credentail set, but returns it only. - * - * @param filename file to load CA cert from - * @return loaded certificate, or NULL - */ - certificate_t* (*load_ca)(stroke_cred_t *this, char *filename); - - /** * Load a peer certificate and serve it through the credential_set. * * @param filename file to load peer cert from @@ -103,6 +94,6 @@ struct stroke_cred_t { /** * Create a stroke_cred instance. */ -stroke_cred_t *stroke_cred_create(); +stroke_cred_t *stroke_cred_create(stroke_ca_t *ca); #endif /** STROKE_CRED_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c index 68b8232bc..c7e4c9c65 100644 --- a/src/libcharon/plugins/stroke/stroke_list.c +++ b/src/libcharon/plugins/stroke/stroke_list.c @@ -647,7 +647,7 @@ METHOD(stroke_list_t, status, void, enumerator->destroy(enumerator); half_open = charon->ike_sa_manager->get_half_open_count( - charon->ike_sa_manager, NULL); + charon->ike_sa_manager, NULL, FALSE); fprintf(out, "Security Associations (%u up, %u connecting):\n", charon->ike_sa_manager->get_count(charon->ike_sa_manager) - half_open, half_open); diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index db7e66f14..29563e32f 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -779,10 +779,10 @@ stroke_socket_t *stroke_socket_create() "%s.plugins.stroke.prevent_loglevel_changes", FALSE, lib->ns), ); - this->cred = stroke_cred_create(); + this->ca = stroke_ca_create(); + this->cred = stroke_cred_create(this->ca); this->attribute = stroke_attribute_create(); this->handler = stroke_handler_create(); - this->ca = stroke_ca_create(this->cred); this->config = stroke_config_create(this->ca, this->cred, this->attribute); this->control = stroke_control_create(); this->list = stroke_list_create(this->attribute); diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_renew_session_job.h b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_renew_session_job.h index 91e8fe404..f1587a1f6 100644 --- a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_renew_session_job.h +++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_renew_session_job.h @@ -15,7 +15,7 @@ /** * @defgroup tnc_ifmap_renew_session_job tnc_ifmap_renew_session_job - * @{ @ingroup cjobs + * @{ @ingroup tnc_ifmap */ #ifndef TNC_IFMAP_RENEW_SESSION_JOB_H_ diff --git a/src/libcharon/plugins/uci/uci_control.c b/src/libcharon/plugins/uci/uci_control.c index cebc389e7..a7d26e67d 100644 --- a/src/libcharon/plugins/uci/uci_control.c +++ b/src/libcharon/plugins/uci/uci_control.c @@ -147,7 +147,7 @@ static void initiate(private_uci_control_t *this, char *name) if (enumerator->enumerate(enumerator, &child_cfg) && charon->controller->initiate(charon->controller, peer_cfg, child_cfg->get_ref(child_cfg), - controller_cb_empty, NULL, 0) == SUCCESS) + controller_cb_empty, NULL, 0, FALSE) == SUCCESS) { write_fifo(this, "connection '%s' established\n", name); } diff --git a/src/libcharon/plugins/updown/updown_listener.c b/src/libcharon/plugins/updown/updown_listener.c index be65d599f..96282bee0 100644 --- a/src/libcharon/plugins/updown/updown_listener.c +++ b/src/libcharon/plugins/updown/updown_listener.c @@ -169,31 +169,34 @@ static void push_dns_env(private_updown_listener_t *this, ike_sa_t *ike_sa, } /** - * Push variables for local virtual IPs + * Push variables for local/remote virtual IPs */ static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa, - char *envp[], u_int count) + char *envp[], u_int count, bool local) { enumerator_t *enumerator; host_t *host; int v4 = 0, v6 = 0; bool first = TRUE; - enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); while (enumerator->enumerate(enumerator, &host)) { if (first) { /* legacy variable for first VIP */ first = FALSE; - push_env(envp, count, "PLUTO_MY_SOURCEIP=%H", host); + push_env(envp, count, "PLUTO_%s_SOURCEIP=%H", + local ? "MY" : "PEER", host); } switch (host->get_family(host)) { case AF_INET: - push_env(envp, count, "PLUTO_MY_SOURCEIP4_%d=%H", ++v4, host); + push_env(envp, count, "PLUTO_%s_SOURCEIP4_%d=%H", + local ? "MY" : "PEER", ++v4, host); break; case AF_INET6: - push_env(envp, count, "PLUTO_MY_SOURCEIP6_%d=%H", ++v6, host); + push_env(envp, count, "PLUTO_%s_SOURCEIP6_%d=%H", + local ? "MY" : "PEER", ++v6, host); break; default: continue; @@ -313,7 +316,8 @@ static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y", ike_sa->get_other_eap_id(ike_sa)); } - push_vip_env(this, ike_sa, envp, countof(envp)); + push_vip_env(this, ike_sa, envp, countof(envp), TRUE); + push_vip_env(this, ike_sa, envp, countof(envp), FALSE); mark = config->get_mark(config, TRUE); if (mark.value) { diff --git a/src/libcharon/plugins/vici/Makefile.am b/src/libcharon/plugins/vici/Makefile.am index b25396085..c99d23e4e 100644 --- a/src/libcharon/plugins/vici/Makefile.am +++ b/src/libcharon/plugins/vici/Makefile.am @@ -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 diff --git a/src/libcharon/plugins/vici/Makefile.in b/src/libcharon/plugins/vici/Makefile.in index b63226daa..1a7870ae9 100644 --- a/src/libcharon/plugins/vici/Makefile.in +++ b/src/libcharon/plugins/vici/Makefile.in @@ -136,7 +136,7 @@ libstrongswan_vici_la_LIBADD = am_libstrongswan_vici_la_OBJECTS = vici_socket.lo vici_message.lo \ vici_builder.lo vici_dispatcher.lo vici_query.lo \ vici_control.lo vici_config.lo vici_cred.lo vici_attribute.lo \ - vici_logger.lo vici_plugin.lo + vici_authority.lo vici_logger.lo vici_plugin.lo libstrongswan_vici_la_OBJECTS = $(am_libstrongswan_vici_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -543,6 +543,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 @@ -736,6 +737,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvici.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_attribute.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_authority.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_builder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vici_control.Plo@am__quote@ diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 0ce4271b0..e20e8ab26 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -259,6 +259,7 @@ Initiates an SA while streaming _control-log_ events. { child = <CHILD_SA configuration name to initiate> timeout = <timeout in seconds before returning> + init-limits = <whether limits may prevent initiating the CHILD_SA> loglevel = <loglevel to issue "control-log" events for> } => { success = <yes or no> @@ -366,6 +367,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 +464,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 +721,82 @@ _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> + } + } + +### ike-updown ### + +The _ike-updown_ event is issued when an IKE_SA is established or terminated. + + { + up = <yes or no> + <IKE_SA config name> = { + <same data as in the list-sas event, but without child-sas section> + } + } + +### ike-rekey ### + +The _ike-rekey_ event is issued when an IKE_SA is rekeyed. + + { + <IKE_SA config name> = { + old = { + <same data as in the list-sas event, but without child-sas section> + } + new = { + <same data as in the list-sas event, but without child-sas section> + } + } + } + +### child-updown ### + +The _child-updown_ event is issued when a CHILD_SA is established or terminated. + + { + up = <yes or no> + <IKE_SA config name> = { + <same data as in the list-sas event, but with only the affected + CHILD_SA in the child-sas section> + } + } + +### child-rekey ### + +The _child-rekey_ event is issued when a CHILD_SA is rekeyed. + + { + <IKE_SA config name> = { + <same data as in the list-sas event, but with the child-sas section + as follows> + child-sas = { + <child-sa-name> = { + old = { + <same data as in the list-sas event> + } + new = { + <same data as in the list-sas event> + } + } + } + } + } # libvici C client library # diff --git a/src/libcharon/plugins/vici/python/LICENSE b/src/libcharon/plugins/vici/python/LICENSE index 111523ca8..54f2158dc 100644 --- a/src/libcharon/plugins/vici/python/LICENSE +++ b/src/libcharon/plugins/vici/python/LICENSE @@ -1,4 +1,6 @@ Copyright (c) 2015 Björn Schuberg +Copyright (c) 2015 Martin Willi +Copyright (c) 2015 Tobias Brunner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/libcharon/plugins/vici/python/vici/exception.py b/src/libcharon/plugins/vici/python/vici/exception.py index 36384e556..757ac51a9 100644 --- a/src/libcharon/plugins/vici/python/vici/exception.py +++ b/src/libcharon/plugins/vici/python/vici/exception.py @@ -8,3 +8,6 @@ class SessionException(Exception): class CommandException(Exception): """Command result exception.""" + +class EventUnknownException(Exception): + """Event unknown exception.""" diff --git a/src/libcharon/plugins/vici/python/vici/session.py b/src/libcharon/plugins/vici/python/vici/session.py index dee58699d..283e3d13d 100644 --- a/src/libcharon/plugins/vici/python/vici/session.py +++ b/src/libcharon/plugins/vici/python/vici/session.py @@ -1,7 +1,7 @@ import collections import socket -from .exception import SessionException, CommandException +from .exception import SessionException, CommandException, EventUnknownException from .protocol import Transport, Packet, Message @@ -197,6 +197,16 @@ class Session(object): """ return self.handler.request("get-pools") + def listen(self, event_types): + """Register and listen for the given events. + + :param event_types: event types to register + :type event_types: list + :return: generator for streamed event responses as (event_type, dict) + :rtype: generator + """ + return self.handler.listen(event_types) + class SessionHandler(object): """Handles client command execution requests over vici.""" @@ -215,6 +225,32 @@ class SessionHandler(object): self.transport.send(packet) return Packet.parse(self.transport.receive()) + def _register_unregister(self, event_type, register): + """Register or unregister for the given event. + + :param event_type: event to register + :type event_type: str + :param register: whether to register or unregister + :type register: bool + """ + if register: + packet = Packet.register_event(event_type) + else: + packet = Packet.unregister_event(event_type) + response = self._communicate(packet) + if response.response_type == Packet.EVENT_UNKNOWN: + raise EventUnknownException( + "Unknown event type '{event}'".format(event=event_type) + ) + elif response.response_type != Packet.EVENT_CONFIRM: + raise SessionException( + "Unexpected response type {type}, " + "expected '{confirm}' (EVENT_CONFIRM)".format( + type=response.response_type, + confirm=Packet.EVENT_CONFIRM, + ) + ) + def request(self, command, message=None): """Send request with an optional message. @@ -265,57 +301,37 @@ class SessionHandler(object): if message is not None: message = Message.serialize(message) - # subscribe to event stream - packet = Packet.register_event(event_stream_type) - response = self._communicate(packet) - - if response.response_type != Packet.EVENT_CONFIRM: - raise SessionException( - "Unexpected response type {type}, " - "expected '{confirm}' (EVENT_CONFIRM)".format( - type=response.response_type, - confirm=Packet.EVENT_CONFIRM, - ) - ) - - # issue command, and read any event messages - packet = Packet.request(command, message) - self.transport.send(packet) - exited = False - while True: - response = Packet.parse(self.transport.receive()) - if response.response_type == Packet.EVENT: - if not exited: - try: - yield Message.deserialize(response.payload) - except GeneratorExit: - exited = True - pass + self._register_unregister(event_stream_type, True); + + try: + packet = Packet.request(command, message) + self.transport.send(packet) + exited = False + while True: + response = Packet.parse(self.transport.receive()) + if response.response_type == Packet.EVENT: + if not exited: + try: + yield Message.deserialize(response.payload) + except GeneratorExit: + exited = True + pass + else: + break + + if response.response_type == Packet.CMD_RESPONSE: + command_response = Message.deserialize(response.payload) else: - break - - if response.response_type == Packet.CMD_RESPONSE: - command_response = Message.deserialize(response.payload) - else: - raise SessionException( - "Unexpected response type {type}, " - "expected '{response}' (CMD_RESPONSE)".format( - type=response.response_type, - response=Packet.CMD_RESPONSE + raise SessionException( + "Unexpected response type {type}, " + "expected '{response}' (CMD_RESPONSE)".format( + type=response.response_type, + response=Packet.CMD_RESPONSE + ) ) - ) - # unsubscribe from event stream - packet = Packet.unregister_event(event_stream_type) - response = self._communicate(packet) - if response.response_type != Packet.EVENT_CONFIRM: - raise SessionException( - "Unexpected response type {type}, " - "expected '{confirm}' (EVENT_CONFIRM)".format( - type=response.response_type, - confirm=Packet.EVENT_CONFIRM, - ) - ) + finally: + self._register_unregister(event_stream_type, False); # evaluate command result, if any if "success" in command_response: @@ -325,3 +341,27 @@ class SessionHandler(object): errmsg=command_response["errmsg"] ) ) + + def listen(self, event_types): + """Register and listen for the given events. + + :param event_types: event types to register + :type event_types: list + :return: generator for streamed event responses as (event_type, dict) + :rtype: generator + """ + for event_type in event_types: + self._register_unregister(event_type, True) + + try: + while True: + response = Packet.parse(self.transport.receive()) + if response.response_type == Packet.EVENT: + try: + yield response.event_type, Message.deserialize(response.payload) + except GeneratorExit: + break + + finally: + for event_type in event_types: + self._register_unregister(event_type, False) diff --git a/src/libcharon/plugins/vici/ruby/lib/vici.rb b/src/libcharon/plugins/vici/ruby/lib/vici.rb index f87e46e69..f8169add0 100644 --- a/src/libcharon/plugins/vici/ruby/lib/vici.rb +++ b/src/libcharon/plugins/vici/ruby/lib/vici.rb @@ -247,7 +247,11 @@ module Vici def recv_all(len) encoding = "" while encoding.length < len do - encoding << @socket.recv(len - encoding.length) + data = @socket.recv(len - encoding.length) + if data.empty? + raise TransportError, "connection closed" + end + encoding << data end encoding end diff --git a/src/libcharon/plugins/vici/suites/test_message.c b/src/libcharon/plugins/vici/suites/test_message.c index e76d27332..045e34fff 100644 --- a/src/libcharon/plugins/vici/suites/test_message.c +++ b/src/libcharon/plugins/vici/suites/test_message.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -355,6 +358,33 @@ START_TEST(test_get_int) } END_TEST +START_TEST(test_get_bool) +{ + vici_message_t *m; + + m = build_getter_msg(); + + ck_assert(m->get_bool(m, TRUE, "key1")); + ck_assert(m->get_bool(m, FALSE, "key1")); + + ck_assert(m->get_bool(m, TRUE, "section1.key2")); + ck_assert(m->get_bool(m, TRUE, "section1.section2.key3")); + ck_assert(m->get_bool(m, TRUE, "section1.key4")); + ck_assert(m->get_bool(m, TRUE, "key5")); + ck_assert(m->get_bool(m, TRUE, "nonexistent")); + ck_assert(m->get_bool(m, TRUE, "n.o.n.e.x.i.s.t.e.n.t")); + + ck_assert(!m->get_bool(m, FALSE, "section1.key2")); + ck_assert(!m->get_bool(m, FALSE, "section1.section2.key3")); + ck_assert(!m->get_bool(m, FALSE, "section1.key4")); + ck_assert(!m->get_bool(m, FALSE, "key5")); + ck_assert(!m->get_bool(m, FALSE, "nonexistent")); + ck_assert(!m->get_bool(m, FALSE, "n.o.n.e.x.i.s.t.e.n.t")); + + m->destroy(m); +} +END_TEST + START_TEST(test_get_value) { vici_message_t *m; @@ -400,6 +430,7 @@ Suite *message_suite_create() tc = tcase_create("convenience getters"); tcase_add_test(tc, test_get_str); tcase_add_test(tc, test_get_int); + tcase_add_test(tc, test_get_bool); tcase_add_test(tc, test_get_value); suite_add_tcase(s, tc); diff --git a/src/libcharon/plugins/vici/vici_authority.c b/src/libcharon/plugins/vici/vici_authority.c new file mode 100644 index 000000000..94a7f68f6 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_authority.c @@ -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, ¤t)) + { + 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; +} diff --git a/src/libcharon/plugins/vici/vici_authority.h b/src/libcharon/plugins/vici/vici_authority.h new file mode 100644 index 000000000..dbeabae62 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_authority.h @@ -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_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index d23259912..ea6d2958a 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -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 @@ -93,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*, @@ -382,7 +391,7 @@ typedef struct { char* updown; bool hostaccess; bool ipcomp; - bool route; + bool policies; ipsec_mode_t mode; u_int32_t replay_window; action_t dpd_action; @@ -417,6 +426,7 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " hostaccess = %u", data->hostaccess); DBG2(DBG_CFG, " ipcomp = %u", data->ipcomp); DBG2(DBG_CFG, " mode = %N", ipsec_mode_names, data->mode); + DBG2(DBG_CFG, " policies = %u", data->policies); if (data->replay_window != REPLAY_UNDEFINED) { DBG2(DBG_CFG, " replay_window = %u", data->replay_window); @@ -1040,15 +1050,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; @@ -1058,18 +1074,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); } /** @@ -1234,6 +1250,7 @@ CALLBACK(child_kv, bool, { "updown", parse_string, &child->updown }, { "hostaccess", parse_bool, &child->hostaccess }, { "mode", parse_mode, &child->mode }, + { "policies", parse_bool, &child->policies }, { "replay_window", parse_uint32, &child->replay_window }, { "rekey_time", parse_time, &child->lft.time.rekey }, { "life_time", parse_time, &child->lft.time.life }, @@ -1264,8 +1281,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, @@ -1341,6 +1358,7 @@ CALLBACK(children_sn, bool, .local_ts = linked_list_create(), .remote_ts = linked_list_create(), .mode = MODE_TUNNEL, + .policies = TRUE, .replay_window = REPLAY_UNDEFINED, .dpd_action = ACTION_NONE, .start_action = ACTION_NONE, @@ -1352,10 +1370,12 @@ CALLBACK(children_sn, bool, .jitter = LFT_UNDEFINED, }, .bytes = { + .rekey = LFT_UNDEFINED, .life = LFT_UNDEFINED, .jitter = LFT_UNDEFINED, }, .packets = { + .rekey = LFT_UNDEFINED, .life = LFT_UNDEFINED, .jitter = LFT_UNDEFINED, }, @@ -1408,6 +1428,15 @@ CALLBACK(children_sn, bool, { child.lft.packets.life = child.lft.packets.rekey * 110 / 100; } + /* if no soft lifetime specified, add one at hard lifetime - 10% */ + if (child.lft.bytes.rekey == LFT_UNDEFINED) + { + child.lft.bytes.rekey = child.lft.bytes.life * 90 / 100; + } + if (child.lft.packets.rekey == LFT_UNDEFINED) + { + child.lft.packets.rekey = child.lft.packets.life * 90 / 100; + } /* if no rand time defined, use difference of hard and soft */ if (child.lft.time.jitter == LFT_UNDEFINED) { @@ -1433,6 +1462,8 @@ CALLBACK(children_sn, bool, child.inactivity, child.reqid, &child.mark_in, &child.mark_out, child.tfc); + cfg->set_mipv6_options(cfg, FALSE, child.policies); + if (child.replay_window != REPLAY_UNDEFINED) { cfg->set_replay_window(cfg, child.replay_window); @@ -1558,7 +1589,7 @@ static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg, DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg)); charon->controller->initiate(charon->controller, peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg), - NULL, NULL, 0); + NULL, NULL, 0, FALSE); break; case ACTION_ROUTE: DBG1(DBG_CFG, "installing '%s'", child_cfg->get_name(child_cfg)); @@ -1958,20 +1989,20 @@ CALLBACK(unload_conn, vici_message_t*, { enumerator_t *enumerator; peer_cfg_t *cfg; + char *conn_name; bool found = FALSE; - char *conn; - conn = message->get_str(message, NULL, "name"); - if (!conn) + conn_name = message->get_str(message, NULL, "name"); + if (!conn_name) { - return create_reply("missing connection name to unload"); + return create_reply("unload: missing connection name"); } this->lock->write_lock(this->lock); enumerator = this->conns->create_enumerator(this->conns); while (enumerator->enumerate(enumerator, &cfg)) { - if (streq(cfg->get_name(cfg), conn)) + if (streq(cfg->get_name(cfg), conn_name)) { this->conns->remove_at(this->conns, enumerator); cfg->destroy(cfg); @@ -1984,7 +2015,7 @@ CALLBACK(unload_conn, vici_message_t*, if (!found) { - return create_reply("connection '%s' not found for unloading", conn); + return create_reply("unload: connection '%s' not found", conn_name); } return create_reply(NULL); } @@ -2042,7 +2073,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; @@ -2058,6 +2090,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); diff --git a/src/libcharon/plugins/vici/vici_config.h b/src/libcharon/plugins/vici/vici_config.h index 820d5f300..c3245bf5c 100644 --- a/src/libcharon/plugins/vici/vici_config.h +++ b/src/libcharon/plugins/vici/vici_config.h @@ -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_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 01d503644..752007c24 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -138,7 +138,7 @@ static child_cfg_t* find_child_cfg(char *name, peer_cfg_t **out) { enumerator_t *enumerator; peer_cfg_t *peer_cfg; - child_cfg_t *child_cfg; + child_cfg_t *child_cfg = NULL; enumerator = charon->backends->create_peer_cfg_enumerator( charon->backends, NULL, NULL, NULL, NULL, IKE_ANY); @@ -163,6 +163,7 @@ CALLBACK(initiate, vici_message_t*, peer_cfg_t *peer_cfg; char *child; u_int timeout; + bool limits; log_info_t log = { .dispatcher = this->dispatcher, .id = id, @@ -170,6 +171,7 @@ CALLBACK(initiate, vici_message_t*, child = request->get_str(request, NULL, "child"); timeout = request->get_int(request, 0, "timeout"); + limits = request->get_bool(request, FALSE, "init-limits"); log.level = request->get_int(request, 1, "loglevel"); if (!child) @@ -184,14 +186,17 @@ CALLBACK(initiate, vici_message_t*, { return send_reply(this, "CHILD_SA config '%s' not found", child); } - switch (charon->controller->initiate(charon->controller, - peer_cfg, child_cfg, (controller_cb_t)log_vici, &log, timeout)) + switch (charon->controller->initiate(charon->controller, peer_cfg, + child_cfg, (controller_cb_t)log_vici, &log, timeout, limits)) { case SUCCESS: return send_reply(this, NULL); case OUT_OF_RES: return send_reply(this, "CHILD_SA '%s' not established after %dms", child, timeout); + case INVALID_STATE: + return send_reply(this, "establishing CHILD_SA '%s' not possible " + "at the moment due to limits", child); case FAILED: default: return send_reply(this, "establishing CHILD_SA '%s' failed", child); diff --git a/src/libcharon/plugins/vici/vici_cred.c b/src/libcharon/plugins/vici/vici_cred.c index d4c02de6d..ffdc034ea 100644 --- a/src/libcharon/plugins/vici/vici_cred.c +++ b/src/libcharon/plugins/vici/vici_cred.c @@ -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, diff --git a/src/libcharon/plugins/vici/vici_cred.h b/src/libcharon/plugins/vici/vici_cred.h index e109a27da..8359c0e88 100644 --- a/src/libcharon/plugins/vici/vici_cred.h +++ b/src/libcharon/plugins/vici/vici_cred.h @@ -31,6 +31,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. */ void (*destroy)(vici_cred_t *this); diff --git a/src/libcharon/plugins/vici/vici_logger.c b/src/libcharon/plugins/vici/vici_logger.c index cffd65bad..6d3584ebd 100644 --- a/src/libcharon/plugins/vici/vici_logger.c +++ b/src/libcharon/plugins/vici/vici_logger.c @@ -18,6 +18,7 @@ #include <daemon.h> #include <threading/mutex.h> +#include <processing/jobs/callback_job.h> typedef struct private_vici_logger_t private_vici_logger_t; @@ -42,11 +43,54 @@ struct private_vici_logger_t { int recursive; /** + * List of messages to raise async events + */ + linked_list_t *queue; + + /** * Mutex to synchronize logging */ mutex_t *mutex; }; +/** + * Async callback to raise events for queued messages + */ +static job_requeue_t raise_events(private_vici_logger_t *this) +{ + vici_message_t *message; + u_int count; + + this->mutex->lock(this->mutex); + count = this->queue->get_count(this->queue); + this->queue->remove_first(this->queue, (void**)&message); + this->mutex->unlock(this->mutex); + + if (count > 0) + { + this->dispatcher->raise_event(this->dispatcher, "log", 0, message); + } + if (count > 1) + { + return JOB_REQUEUE_DIRECT; + } + return JOB_REQUEUE_NONE; +} + +/** + * Queue a message for async processing + */ +static void queue_messsage(private_vici_logger_t *this, vici_message_t *message) +{ + this->queue->insert_last(this->queue, message); + if (this->queue->get_count(this->queue) == 1) + { + lib->processor->queue_job(lib->processor, (job_t*) + callback_job_create((callback_job_cb_t)raise_events, + this, NULL, NULL)); + } +} + METHOD(logger_t, log_, void, private_vici_logger_t *this, debug_t group, level_t level, int thread, ike_sa_t* ike_sa, const char *msg) @@ -75,7 +119,7 @@ METHOD(logger_t, log_, void, message = builder->finalize(builder); if (message) { - this->dispatcher->raise_event(this->dispatcher, "log", 0, message); + queue_messsage(this, message); } } this->recursive--; @@ -101,6 +145,7 @@ METHOD(vici_logger_t, destroy, void, private_vici_logger_t *this) { manage_commands(this, FALSE); + this->queue->destroy_offset(this->queue, offsetof(vici_message_t, destroy)); this->mutex->destroy(this->mutex); free(this); } @@ -121,6 +166,7 @@ vici_logger_t *vici_logger_create(vici_dispatcher_t *dispatcher) .destroy = _destroy, }, .dispatcher = dispatcher, + .queue = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), ); diff --git a/src/libcharon/plugins/vici/vici_message.c b/src/libcharon/plugins/vici/vici_message.c index e79fbc8d3..fb6e8a1ab 100644 --- a/src/libcharon/plugins/vici/vici_message.c +++ b/src/libcharon/plugins/vici/vici_message.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -385,6 +388,41 @@ METHOD(vici_message_t, get_int, int, return val; } +METHOD(vici_message_t, vget_bool, bool, + private_vici_message_t *this, bool def, char *fmt, va_list args) +{ + chunk_t value; + bool found; + char buf[16]; + + found = find_value(this, &value, fmt, args); + if (found) + { + if (value.len == 0) + { + return def; + } + if (chunk_printable(value, NULL, 0)) + { + snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr); + return settings_value_as_bool(buf, def); + } + } + return def; +} + +METHOD(vici_message_t, get_bool, bool, + private_vici_message_t *this, bool def, char *fmt, ...) +{ + va_list args; + bool val; + + va_start(args, fmt); + val = vget_bool(this, def, fmt, args); + va_end(args); + return val; +} + METHOD(vici_message_t, vget_value, chunk_t, private_vici_message_t *this, chunk_t def, char *fmt, va_list args) { @@ -633,6 +671,8 @@ vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup) .vget_str = _vget_str, .get_int = _get_int, .vget_int = _vget_int, + .get_bool = _get_bool, + .vget_bool = _vget_bool, .get_value = _get_value, .vget_value = _vget_value, .get_encoding = _get_encoding, diff --git a/src/libcharon/plugins/vici/vici_message.h b/src/libcharon/plugins/vici/vici_message.h index 1a89cf829..d47e7a0f9 100644 --- a/src/libcharon/plugins/vici/vici_message.h +++ b/src/libcharon/plugins/vici/vici_message.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -15,7 +18,7 @@ /** * @defgroup vici_message vici_message - * @{ @ingroup vici_dispatcher + * @{ @ingroup vici */ #ifndef VICI_MESSAGE_H_ @@ -138,6 +141,26 @@ struct vici_message_t { int (*vget_int)(vici_message_t *this, int def, char *fmt, va_list args); /** + * Get the value of a key/value pair as boolean. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return value + */ + bool (*get_bool)(vici_message_t *this, bool def, char *fmt, ...); + + /** + * Get the value of a key/value pair as boolean, va_list variant + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return value + */ + bool (*vget_bool)(vici_message_t *this, bool def, char *fmt, va_list args); + + /** * Get the raw value of a key/value pair. * * @param def default value if not found diff --git a/src/libcharon/plugins/vici/vici_plugin.c b/src/libcharon/plugins/vici/vici_plugin.c index 7ae58a317..53ed8cdfb 100644 --- a/src/libcharon/plugins/vici/vici_plugin.c +++ b/src/libcharon/plugins/vici/vici_plugin.c @@ -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> @@ -80,6 +84,11 @@ struct private_vici_plugin_t { vici_cred_t *cred; /** + * Certification Authority backend + */ + vici_authority_t *authority; + + /** * Configuration backend */ vici_config_t *config; @@ -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); diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index d94d760b9..98d264fca 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -929,7 +929,7 @@ CALLBACK(stats, vici_message_t*, charon->ike_sa_manager->get_count(charon->ike_sa_manager)); b->add_kv(b, "half-open", "%u", charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, - NULL)); + NULL, FALSE)); b->end_section(b); b->begin_list(b, "plugins"); @@ -1031,7 +1031,9 @@ static void manage_commands(private_vici_query_t *this, bool reg) this->dispatcher->manage_event(this->dispatcher, "list-conn", reg); this->dispatcher->manage_event(this->dispatcher, "list-cert", reg); this->dispatcher->manage_event(this->dispatcher, "ike-updown", reg); + this->dispatcher->manage_event(this->dispatcher, "ike-rekey", reg); this->dispatcher->manage_event(this->dispatcher, "child-updown", reg); + this->dispatcher->manage_event(this->dispatcher, "child-rekey", reg); manage_command(this, "list-sas", list_sas, reg); manage_command(this, "list-policies", list_policies, reg); manage_command(this, "list-conns", list_conns, reg); @@ -1054,10 +1056,14 @@ METHOD(listener_t, ike_updown, bool, now = time_monotonic(NULL); b = vici_builder_create(); + + if (up) + { + b->add_kv(b, "up", "yes"); + } + b->begin_section(b, ike_sa->get_name(ike_sa)); list_ike(this, b, ike_sa, now); - b->begin_section(b, "child-sas"); - b->end_section(b); b->end_section(b); this->dispatcher->raise_event(this->dispatcher, @@ -1066,6 +1072,35 @@ METHOD(listener_t, ike_updown, bool, return TRUE; } +METHOD(listener_t, ike_rekey, bool, + private_vici_query_t *this, ike_sa_t *old, ike_sa_t *new) +{ + vici_builder_t *b; + time_t now; + + if (!this->dispatcher->has_event_listeners(this->dispatcher, "ike-rekey")) + { + return TRUE; + } + + now = time_monotonic(NULL); + + b = vici_builder_create(); + b->begin_section(b, old->get_name(old)); + b->begin_section(b, "old"); + list_ike(this, b, old, now); + b->end_section(b); + b->begin_section(b, "new"); + list_ike(this, b, new, now); + b->end_section(b); + b->end_section(b); + + this->dispatcher->raise_event(this->dispatcher, + "ike-rekey", 0, b->finalize(b)); + + return TRUE; +} + METHOD(listener_t, child_updown, bool, private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up) { @@ -1080,6 +1115,11 @@ METHOD(listener_t, child_updown, bool, now = time_monotonic(NULL); b = vici_builder_create(); + if (up) + { + b->add_kv(b, "up", "yes"); + } + b->begin_section(b, ike_sa->get_name(ike_sa)); list_ike(this, b, ike_sa, now); b->begin_section(b, "child-sas"); @@ -1097,6 +1137,45 @@ METHOD(listener_t, child_updown, bool, return TRUE; } +METHOD(listener_t, child_rekey, bool, + private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *old, + child_sa_t *new) +{ + vici_builder_t *b; + time_t now; + + if (!this->dispatcher->has_event_listeners(this->dispatcher, "child-rekey")) + { + return TRUE; + } + + now = time_monotonic(NULL); + b = vici_builder_create(); + + b->begin_section(b, ike_sa->get_name(ike_sa)); + list_ike(this, b, ike_sa, now); + b->begin_section(b, "child-sas"); + + b->begin_section(b, old->get_name(old)); + + b->begin_section(b, "old"); + list_child(this, b, old, now); + b->end_section(b); + b->begin_section(b, "new"); + list_child(this, b, new, now); + b->end_section(b); + + b->end_section(b); + + b->end_section(b); + b->end_section(b); + + this->dispatcher->raise_event(this->dispatcher, + "child-rekey", 0, b->finalize(b)); + + return TRUE; +} + METHOD(vici_query_t, destroy, void, private_vici_query_t *this) { @@ -1115,7 +1194,9 @@ vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher) .public = { .listener = { .ike_updown = _ike_updown, + .ike_rekey = _ike_rekey, .child_updown = _child_updown, + .child_rekey = _child_rekey, }, .destroy = _destroy, }, diff --git a/src/libcharon/plugins/whitelist/whitelist_listener.c b/src/libcharon/plugins/whitelist/whitelist_listener.c index d0357b410..7e5b2f4e0 100644 --- a/src/libcharon/plugins/whitelist/whitelist_listener.c +++ b/src/libcharon/plugins/whitelist/whitelist_listener.c @@ -52,7 +52,7 @@ struct private_whitelist_listener_t { */ static u_int hash(identification_t *key) { - return chunk_hash(key->get_encoding(key)); + return key->hash(key, 0); } /** |