diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2015-10-22 11:43:58 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2015-10-22 11:43:58 +0200 |
commit | 5dca9ea0e2931f0e2a056c7964d311bcc30a01b8 (patch) | |
tree | 037f1ec5bb860846938ddcf29771c24e9c529be0 /src/libcharon | |
parent | b238cf34df3fe4476ae6b7012e7cb3e9769d4d51 (diff) | |
download | vyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.tar.gz vyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.zip |
Imported Upstream version 5.3.3
Diffstat (limited to 'src/libcharon')
86 files changed, 3025 insertions, 472 deletions
diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index 9464ceb5d..dee9e4c29 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -513,6 +514,52 @@ static void parse_addresses(char *str, linked_list_t *hosts, /** * Described in header. */ +int ike_cfg_get_family(ike_cfg_t *cfg, bool local) +{ + private_ike_cfg_t *this = (private_ike_cfg_t*)cfg; + enumerator_t *enumerator; + host_t *host; + char *str; + int family = AF_UNSPEC; + + if (local) + { + enumerator = this->my_hosts->create_enumerator(this->my_hosts); + } + else + { + enumerator = this->other_hosts->create_enumerator(this->other_hosts); + } + while (enumerator->enumerate(enumerator, &str)) + { + if (streq(str, "%any")) + { /* ignore %any as its family is undetermined */ + continue; + } + host = host_create_from_string(str, 0); + if (host) + { + if (family == AF_UNSPEC) + { + family = host->get_family(host); + } + else if (family != host->get_family(host)) + { + /* more than one address family defined */ + family = AF_UNSPEC; + host->destroy(host); + break; + } + } + DESTROY_IF(host); + } + enumerator->destroy(enumerator); + return family; +} + +/** + * Described in header. + */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, u_int16_t my_port, char *other, u_int16_t other_port, diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index adfcabf70..a72960f4f 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -160,7 +160,7 @@ struct ike_cfg_t { * * Returned list and its proposals must be destroyed after use. * - * @return list containing all the proposals + * @return list containing all the proposals */ linked_list_t* (*get_proposals) (ike_cfg_t *this); @@ -247,11 +247,22 @@ struct ike_cfg_t { * @param other_port IKE port to use as dest, 500 uses IKEv2 port floating * @param fragmentation use IKEv1 fragmentation * @param dscp DSCP value to send IKE packets with - * @return ike_cfg_t object. + * @return ike_cfg_t object. */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, u_int16_t my_port, char *other, u_int16_t other_port, fragmentation_t fragmentation, u_int8_t dscp); +/** + * Determine the address family of the local or remtoe address(es). If multiple + * families are configured AF_UNSPEC is returned. %any is ignored (%any4|6 are + * not though). + * + * @param this ike config to check + * @param local TRUE to check local addresses, FALSE for remote + * @return address family of address(es) if distinct + */ +int ike_cfg_get_family(ike_cfg_t *this, bool local); + #endif /** IKE_CFG_H_ @}*/ diff --git a/src/libcharon/control/controller.c b/src/libcharon/control/controller.c index fd8349e2f..6dd54b473 100644 --- a/src/libcharon/control/controller.c +++ b/src/libcharon/control/controller.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Tobias Brunner + * Copyright (C) 2011-2015 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Copyright (C) 2011 revosec AG * Hochschule fuer Technik Rapperswil @@ -116,6 +116,11 @@ struct interface_listener_t { * spinlock to update the IKE_SA handle properly */ spinlock_t *lock; + + /** + * whether to check limits + */ + bool limits; }; @@ -358,7 +363,6 @@ METHOD(job_t, initiate_execute, job_requeue_t, listener->child_cfg->destroy(listener->child_cfg); peer_cfg->destroy(peer_cfg); listener->status = FAILED; - /* release listener */ listener_done(listener); return JOB_REQUEUE_NONE; } @@ -372,6 +376,49 @@ METHOD(job_t, initiate_execute, job_requeue_t, } peer_cfg->destroy(peer_cfg); + if (listener->limits && ike_sa->get_state(ike_sa) == IKE_CREATED) + { /* only check if we are not reusing an IKE_SA */ + u_int half_open, limit_half_open, limit_job_load; + + half_open = charon->ike_sa_manager->get_half_open_count( + charon->ike_sa_manager, NULL, FALSE); + limit_half_open = lib->settings->get_int(lib->settings, + "%s.init_limit_half_open", 0, lib->ns); + limit_job_load = lib->settings->get_int(lib->settings, + "%s.init_limit_job_load", 0, lib->ns); + if (limit_half_open && half_open >= limit_half_open) + { + DBG1(DBG_IKE, "abort IKE_SA initiation, half open IKE_SA count of " + "%d exceeds limit of %d", half_open, limit_half_open); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + listener->child_cfg->destroy(listener->child_cfg); + listener->status = INVALID_STATE; + listener_done(listener); + return JOB_REQUEUE_NONE; + } + if (limit_job_load) + { + u_int jobs = 0, i; + + for (i = 0; i < JOB_PRIO_MAX; i++) + { + jobs += lib->processor->get_job_load(lib->processor, i); + } + if (jobs > limit_job_load) + { + DBG1(DBG_IKE, "abort IKE_SA initiation, job load of %d exceeds " + "limit of %d", jobs, limit_job_load); + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + listener->child_cfg->destroy(listener->child_cfg); + listener->status = INVALID_STATE; + listener_done(listener); + return JOB_REQUEUE_NONE; + } + } + } + if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS) { if (!listener->logger.callback) @@ -391,7 +438,7 @@ METHOD(job_t, initiate_execute, job_requeue_t, METHOD(controller_t, initiate, status_t, private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg, - controller_cb_t callback, void *param, u_int timeout) + controller_cb_t callback, void *param, u_int timeout, bool limits) { interface_job_t *job; status_t status; @@ -414,6 +461,7 @@ METHOD(controller_t, initiate, status_t, .child_cfg = child_cfg, .peer_cfg = peer_cfg, .lock = spinlock_create(), + .limits = limits, }, .public = { .execute = _initiate_execute, diff --git a/src/libcharon/control/controller.h b/src/libcharon/control/controller.h index 02f4ebb2b..5ffeac522 100644 --- a/src/libcharon/control/controller.h +++ b/src/libcharon/control/controller.h @@ -82,15 +82,18 @@ struct controller_t { * @param cb logging callback * @param param parameter to include in each call of cb * @param timeout timeout in ms to wait for callbacks, 0 to disable + * @param limits whether to check limits regarding IKE_SA initiation * @return * - SUCCESS, if CHILD_SA established * - FAILED, if setup failed * - NEED_MORE, if callback returned FALSE * - OUT_OF_RES if timed out + * - INVALID_STATE if limits prevented initiation */ status_t (*initiate)(controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg, - controller_cb_t callback, void *param, u_int timeout); + controller_cb_t callback, void *param, u_int timeout, + bool limits); /** * Terminate an IKE_SA and all of its CHILD_SAs. diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index b1b8f57f0..316be7611 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -462,6 +462,10 @@ static void destroy(private_daemon_t *this) { this->public.traps->flush(this->public.traps); } + if (this->public.shunts) + { + this->public.shunts->flush(this->public.shunts); + } if (this->public.sender) { this->public.sender->flush(this->public.sender); diff --git a/src/libcharon/encoding/payloads/fragment_payload.c b/src/libcharon/encoding/payloads/fragment_payload.c index b861fcc68..7f158f548 100644 --- a/src/libcharon/encoding/payloads/fragment_payload.c +++ b/src/libcharon/encoding/payloads/fragment_payload.c @@ -222,4 +222,4 @@ fragment_payload_t *fragment_payload_create_from_data(u_int8_t num, bool last, this->data = chunk_clone(data); this->payload_length = get_header_length(this) + data.len; return &this->public; -}
\ No newline at end of file +} diff --git a/src/libcharon/encoding/payloads/proposal_substructure.c b/src/libcharon/encoding/payloads/proposal_substructure.c index 48dcfeb24..65ce667c7 100644 --- a/src/libcharon/encoding/payloads/proposal_substructure.c +++ b/src/libcharon/encoding/payloads/proposal_substructure.c @@ -914,6 +914,11 @@ static void add_to_proposal_v1_ike(proposal_t *proposal, if (encr != ENCR_UNDEFINED) { + if (encr == ENCR_AES_CBC && !key_length) + { /* some implementations don't send a Key Length attribute for + * AES-128, early drafts of RFC 3602 allowed that */ + key_length = 128; + } proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, key_length); } } @@ -962,6 +967,12 @@ static void add_to_proposal_v1(proposal_t *proposal, transform->get_transform_id(transform)); if (encr) { + if (encr == ENCR_AES_CBC && !key_length) + { /* some implementations don't send a Key Length attribute for + * AES-128, early drafts of RFC 3602 allowed that for IKE, some + * also seem to do it for ESP */ + key_length = 128; + } proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, key_length); } diff --git a/src/libcharon/network/receiver.c b/src/libcharon/network/receiver.c index 6902c4847..a2f2016ff 100644 --- a/src/libcharon/network/receiver.c +++ b/src/libcharon/network/receiver.c @@ -322,16 +322,18 @@ static bool cookie_required(private_receiver_t *this, */ static bool drop_ike_sa_init(private_receiver_t *this, message_t *message) { - u_int half_open; + u_int half_open, half_open_r; u_int32_t now; now = time_monotonic(NULL); half_open = charon->ike_sa_manager->get_half_open_count( - charon->ike_sa_manager, NULL); + charon->ike_sa_manager, NULL, FALSE); + half_open_r = charon->ike_sa_manager->get_half_open_count( + charon->ike_sa_manager, NULL, TRUE); /* check for cookies in IKEv2 */ if (message->get_major_version(message) == IKEV2_MAJOR_VERSION && - cookie_required(this, half_open, now) && !check_cookie(this, message)) + cookie_required(this, half_open_r, now) && !check_cookie(this, message)) { chunk_t cookie; @@ -372,7 +374,7 @@ static bool drop_ike_sa_init(private_receiver_t *this, message_t *message) /* check if peer has too many IKE_SAs half open */ if (this->block_threshold && charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager, - message->get_source(message)) >= this->block_threshold) + message->get_source(message), TRUE) >= this->block_threshold) { DBG1(DBG_NET, "ignoring IKE_SA setup from %H, " "peer too aggressive", message->get_source(message)); @@ -381,7 +383,7 @@ static bool drop_ike_sa_init(private_receiver_t *this, message_t *message) /* check if global half open IKE_SA limit reached */ if (this->init_limit_half_open && - half_open >= this->init_limit_half_open) + half_open >= this->init_limit_half_open) { DBG1(DBG_NET, "ignoring IKE_SA setup from %H, half open IKE_SA " "count of %d exceeds limit of %d", message->get_source(message), @@ -542,7 +544,9 @@ static job_requeue_t receive_packets(private_receiver_t *this) if (message->get_request(message) && message->get_exchange_type(message) == IKE_SA_INIT) { - if (this->initiator_only || drop_ike_sa_init(this, message)) + id = message->get_ike_sa_id(message); + if (this->initiator_only || !id->is_initiator(id) || + drop_ike_sa_init(this, message)) { message->destroy(message); return JOB_REQUEUE_DIRECT; 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); } /** diff --git a/src/libcharon/processing/jobs/initiate_mediation_job.c b/src/libcharon/processing/jobs/initiate_mediation_job.c index 17ab83053..5b5fb9d98 100644 --- a/src/libcharon/processing/jobs/initiate_mediation_job.c +++ b/src/libcharon/processing/jobs/initiate_mediation_job.c @@ -119,8 +119,8 @@ METHOD(job_t, initiate, job_requeue_t, /* we need an additional reference because initiate consumes one */ mediation_cfg->get_ref(mediation_cfg); - if (charon->controller->initiate(charon->controller, mediation_cfg, - NULL, (controller_cb_t)initiate_callback, this, 0) != SUCCESS) + if (charon->controller->initiate(charon->controller, mediation_cfg, NULL, + (controller_cb_t)initiate_callback, this, 0, FALSE) != SUCCESS) { mediation_cfg->destroy(mediation_cfg); mediated_cfg->destroy(mediated_cfg); diff --git a/src/libcharon/processing/jobs/process_message_job.c b/src/libcharon/processing/jobs/process_message_job.c index a6795e766..31f048db6 100644 --- a/src/libcharon/processing/jobs/process_message_job.c +++ b/src/libcharon/processing/jobs/process_message_job.c @@ -91,16 +91,26 @@ METHOD(job_t, get_priority, job_priority_t, { case IKE_AUTH: /* IKE auth is rather expensive and often blocking, low priority */ + case AGGRESSIVE: + case ID_PROT: + /* AM is basically IKE_SA_INIT/IKE_AUTH combined (without EAP/XAuth) + * MM is similar, but stretched out more */ return JOB_PRIO_LOW; case INFORMATIONAL: + case INFORMATIONAL_V1: /* INFORMATIONALs are inexpensive, for DPD we should have low * reaction times */ return JOB_PRIO_HIGH; case IKE_SA_INIT: - case CREATE_CHILD_SA: - default: /* IKE_SA_INIT is expensive, but we will drop them in the receiver * if we are overloaded */ + case CREATE_CHILD_SA: + case QUICK_MODE: + /* these may require DH, but if not they are relatively cheap */ + case TRANSACTION: + /* these are mostly cheap, however, if XAuth via RADIUS is used + * they may block */ + default: return JOB_PRIO_MEDIUM; } } diff --git a/src/libcharon/processing/jobs/rekey_child_sa_job.c b/src/libcharon/processing/jobs/rekey_child_sa_job.c index 8f17d39ab..057876b33 100644 --- a/src/libcharon/processing/jobs/rekey_child_sa_job.c +++ b/src/libcharon/processing/jobs/rekey_child_sa_job.c @@ -67,7 +67,10 @@ METHOD(job_t, execute, job_requeue_t, } else { - ike_sa->rekey_child_sa(ike_sa, this->protocol, this->spi); + if (ike_sa->get_state(ike_sa) != IKE_PASSIVE) + { + ike_sa->rekey_child_sa(ike_sa, this->protocol, this->spi); + } charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } return JOB_REQUEUE_NONE; diff --git a/src/libcharon/processing/jobs/start_action_job.c b/src/libcharon/processing/jobs/start_action_job.c index 981473b5c..5e88ac230 100644 --- a/src/libcharon/processing/jobs/start_action_job.c +++ b/src/libcharon/processing/jobs/start_action_job.c @@ -61,7 +61,7 @@ METHOD(job_t, execute, job_requeue_t, 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_JOB, "start action: route '%s'", name); diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 94cf07c33..73f2ec9d3 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2015 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter @@ -106,6 +106,11 @@ struct private_child_sa_t { */ bool reqid_allocated; + /** + * Is the reqid statically configured + */ + bool static_reqid; + /* * Unique CHILD_SA identifier */ @@ -698,7 +703,7 @@ METHOD(child_sa_t, install, status_t, this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS, &esn, NULL); - if (!this->reqid_allocated && !this->reqid) + if (!this->reqid_allocated && !this->static_reqid) { status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface, my_ts, other_ts, this->mark_in, this->mark_out, @@ -826,7 +831,7 @@ METHOD(child_sa_t, add_policies, status_t, traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; - if (!this->reqid_allocated && !this->reqid) + if (!this->reqid_allocated && !this->static_reqid) { /* trap policy, get or confirm reqid */ status = hydra->kernel_interface->alloc_reqid( @@ -1305,6 +1310,10 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->reqid = charon->traps->find_reqid(charon->traps, config); } } + else + { + this->static_reqid = TRUE; + } /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ if (config->get_mode(config) == MODE_TRANSPORT && diff --git a/src/libcharon/sa/eap/eap_method.c b/src/libcharon/sa/eap/eap_method.c index a05e8c59a..9ce6ecf00 100644 --- a/src/libcharon/sa/eap/eap_method.c +++ b/src/libcharon/sa/eap/eap_method.c @@ -30,7 +30,8 @@ bool eap_method_register(plugin_t *plugin, plugin_feature_t *feature, { if (reg) { - charon->eap->add_method(charon->eap, feature->arg.eap, 0, + charon->eap->add_method(charon->eap, feature->arg.eap.type, + feature->arg.eap.vendor, feature->type == FEATURE_EAP_SERVER ? EAP_SERVER : EAP_PEER, (eap_constructor_t)data); } diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 3aafa4c13..dcf9d5f2c 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2014 Tobias Brunner + * Copyright (C) 2006-2015 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -487,8 +487,9 @@ METHOD(ike_sa_t, send_keepalive, void, send_keepalive_job_t *job; time_t last_out, now, diff; - if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0) - { /* disable keep alives if we are not NATed anymore */ + if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0 || + this->state == IKE_PASSIVE) + { /* disable keep alives if we are not NATed anymore, or we are passive */ return; } @@ -651,7 +652,7 @@ METHOD(ike_sa_t, get_state, ike_sa_state_t, METHOD(ike_sa_t, set_state, void, private_ike_sa_t *this, ike_sa_state_t state) { - bool trigger_dpd = FALSE; + bool trigger_dpd = FALSE, keepalives = FALSE; DBG2(DBG_IKE, "IKE_SA %s[%d] state change: %N => %N", get_name(this), this->unique_id, @@ -722,6 +723,10 @@ METHOD(ike_sa_t, set_state, void, * so yet, so prevent that. */ this->stats[STAT_INBOUND] = this->stats[STAT_ESTABLISHED]; } + if (this->state == IKE_PASSIVE) + { + keepalives = TRUE; + } } break; } @@ -742,6 +747,10 @@ METHOD(ike_sa_t, set_state, void, DBG1(DBG_IKE, "DPD not supported by peer, disabled"); } } + if (keepalives) + { + send_keepalive(this); + } } METHOD(ike_sa_t, reset, void, @@ -1200,6 +1209,19 @@ static void resolve_hosts(private_ike_sa_t *this) break; } + /* if an IP address is set locally, use the same family to resolve remote */ + if (family == AF_UNSPEC && !this->remote_host) + { + if (this->local_host) + { + family = this->local_host->get_family(this->local_host); + } + else + { + family = ike_cfg_get_family(this->ike_cfg, TRUE); + } + } + if (this->remote_host) { host = this->remote_host->clone(this->remote_host); @@ -1211,7 +1233,18 @@ static void resolve_hosts(private_ike_sa_t *this) } if (host) { - set_other_host(this, host); + if (!host->is_anyaddr(host) || + this->other_host->is_anyaddr(this->other_host)) + { /* don't set to %any if we currently have an address, but the + * address family might have changed */ + set_other_host(this, host); + } + else + { /* reuse the original port as some implementations might not like + * initial IKE messages on other ports */ + this->other_host->set_port(this->other_host, host->get_port(host)); + host->destroy(host); + } } if (this->local_host) diff --git a/src/libcharon/sa/ike_sa_id.c b/src/libcharon/sa/ike_sa_id.c index 0f0f1ab63..e52086483 100644 --- a/src/libcharon/sa/ike_sa_id.c +++ b/src/libcharon/sa/ike_sa_id.c @@ -18,7 +18,7 @@ #include "ike_sa_id.h" #include <stdio.h> - +#include <encoding/payloads/ike_header.h> typedef struct private_ike_sa_id_t private_ike_sa_id_t; @@ -90,6 +90,8 @@ METHOD(ike_sa_id_t, equals, bool, return FALSE; } return this->ike_version == other->ike_version && + (this->ike_version == IKEV1_MAJOR_VERSION || + this->is_initiator_flag == other->is_initiator_flag) && this->initiator_spi == other->initiator_spi && this->responder_spi == other->responder_spi; } diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 938f7848f..37d69874d 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2005-2011 Martin Willi * Copyright (C) 2011 revosec AG - * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -157,6 +157,8 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id) } if ((id->get_responder_spi(id) == 0 || entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) && + (id->get_ike_version(id) == IKEV1_MAJOR_VERSION || + id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id)) && id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id)) { /* this is TRUE for IKE_SAs that we initiated but have not yet received a response */ @@ -204,6 +206,9 @@ struct half_open_t { /** the number of half-open IKE_SAs with that host */ u_int count; + + /** the number of half-open IKE_SAs we responded to with that host */ + u_int count_responder; }; /** @@ -359,6 +364,11 @@ struct private_ike_sa_manager_t { refcount_t half_open_count; /** + * Total number of half-open IKE_SAs as responder. + */ + refcount_t half_open_count_responder; + + /** * Hash table with connected_peers_t objects. */ table_item_t **connected_peers_table; @@ -384,6 +394,11 @@ struct private_ike_sa_manager_t { rng_t *rng; /** + * Lock to access the RNG instance + */ + rwlock_t *rng_lock; + + /** * reuse existing IKE_SAs in checkout_by_config */ bool reuse_ikesa; @@ -730,9 +745,11 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) table_item_t *item; u_int row, segment; rwlock_t *lock; + ike_sa_id_t *ike_id; half_open_t *half_open; chunk_t addr; + ike_id = entry->ike_sa_id; addr = entry->other->get_address(entry->other); row = chunk_hash(addr) & this->table_mask; segment = row & this->segment_mask; @@ -745,7 +762,6 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) if (chunk_equals(addr, half_open->other)) { - half_open->count++; break; } item = item->next; @@ -755,7 +771,6 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) { INIT(half_open, .other = chunk_clone(addr), - .count = 1, ); INIT(item, .value = half_open, @@ -763,8 +778,14 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) ); this->half_open_table[row] = item; } - this->half_open_segments[segment].count++; + half_open->count++; ref_get(&this->half_open_count); + if (!ike_id->is_initiator(ike_id)) + { + half_open->count_responder++; + ref_get(&this->half_open_count_responder); + } + this->half_open_segments[segment].count++; lock->unlock(lock); } @@ -776,8 +797,10 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) table_item_t *item, *prev = NULL; u_int row, segment; rwlock_t *lock; + ike_sa_id_t *ike_id; chunk_t addr; + ike_id = entry->ike_sa_id; addr = entry->other->get_address(entry->other); row = chunk_hash(addr) & this->table_mask; segment = row & this->segment_mask; @@ -790,6 +813,12 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) if (chunk_equals(addr, half_open->other)) { + if (!ike_id->is_initiator(ike_id)) + { + half_open->count_responder--; + ignore_result(ref_put(&this->half_open_count_responder)); + } + ignore_result(ref_put(&this->half_open_count)); if (--half_open->count == 0) { if (prev) @@ -804,7 +833,6 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) free(item); } this->half_open_segments[segment].count--; - ignore_result(ref_put(&this->half_open_count)); break; } prev = item; @@ -943,12 +971,14 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this) { u_int64_t spi; - if (this->rng && - this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi)) + this->rng_lock->read_lock(this->rng_lock); + if (!this->rng || + !this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi)) { - return spi; + spi = 0; } - return 0; + this->rng_lock->unlock(this->rng_lock); + return spi; } /** @@ -1563,7 +1593,6 @@ METHOD(ike_sa_manager_t, checkin, void, put_half_open(this, entry); } else if (!entry->half_open && - !entry->ike_sa_id->is_initiator(entry->ike_sa_id) && ike_sa->get_state(ike_sa) == IKE_CONNECTING) { /* this is a new half-open SA */ @@ -1579,6 +1608,12 @@ METHOD(ike_sa_manager_t, checkin, void, entry = entry_create(); entry->ike_sa_id = ike_sa_id->clone(ike_sa_id); entry->ike_sa = ike_sa; + if (ike_sa->get_state(ike_sa) == IKE_CONNECTING) + { + entry->half_open = TRUE; + entry->other = other->clone(other); + put_half_open(this, entry); + } segment = put_entry(this, entry); } @@ -1937,7 +1972,7 @@ METHOD(ike_sa_manager_t, get_count, u_int, } METHOD(ike_sa_manager_t, get_half_open_count, u_int, - private_ike_sa_manager_t *this, host_t *ip) + private_ike_sa_manager_t *this, host_t *ip, bool responder_only) { table_item_t *item; u_int row, segment; @@ -1959,7 +1994,8 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int, if (chunk_equals(addr, half_open->other)) { - count = half_open->count; + count = responder_only ? half_open->count_responder + : half_open->count; break; } item = item->next; @@ -1968,7 +2004,8 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int, } else { - count = (u_int)ref_cur(&this->half_open_count); + count = responder_only ? (u_int)ref_cur(&this->half_open_count_responder) + : (u_int)ref_cur(&this->half_open_count); } return count; } @@ -2055,8 +2092,10 @@ METHOD(ike_sa_manager_t, flush, void, charon->bus->set_sa(charon->bus, NULL); unlock_all_segments(this); + this->rng_lock->write_lock(this->rng_lock); this->rng->destroy(this->rng); this->rng = NULL; + this->rng_lock->unlock(this->rng_lock); } METHOD(ike_sa_manager_t, destroy, void, @@ -2081,6 +2120,7 @@ METHOD(ike_sa_manager_t, destroy, void, free(this->connected_peers_segments); free(this->init_hashes_segments); + this->rng_lock->destroy(this->rng_lock); free(this); } @@ -2138,6 +2178,7 @@ ike_sa_manager_t *ike_sa_manager_create() free(this); return NULL; } + this->rng_lock = rwlock_create(RWLOCK_TYPE_DEFAULT); this->ikesa_limit = lib->settings->get_int(lib->settings, "%s.ikesa_limit", 0, lib->ns); diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h index f259d8e56..3ea928ea5 100644 --- a/src/libcharon/sa/ike_sa_manager.h +++ b/src/libcharon/sa/ike_sa_manager.h @@ -216,14 +216,15 @@ struct ike_sa_manager_t { * To prevent the server from resource exhaustion, cookies and other * mechanisms are used. The number of half open IKE_SAs is a good * indicator to see if a peer is flooding the server. - * If a host is supplied, only the number of half open IKE_SAs initiated - * from this IP are counted. - * Only SAs for which we are the responder are counted. + * If a host is supplied, only the number of half open IKE_SAs with this IP + * are counted. * * @param ip NULL for all, IP for half open IKE_SAs with IP + * @param responder_only TRUE to return only the number of responding SAs * @return number of half open IKE_SAs */ - u_int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip); + u_int (*get_half_open_count)(ike_sa_manager_t *this, host_t *ip, + bool responder_only); /** * Delete all existing IKE_SAs and destroy them immediately. diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index c968b2a9c..b7047e8fc 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -404,7 +404,7 @@ static auth_method_t get_pubkey_method(private_phase1_t *this, auth_cfg_t *auth) id = (identification_t*)auth->get(auth, AUTH_RULE_IDENTITY); if (id) { - private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, NULL); + private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth); if (private) { switch (private->get_type(private)) diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index ed547c4c2..678f99df1 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2014 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -901,6 +901,34 @@ static bool process_dpd(private_task_manager_t *this, message_t *message) } /** + * Check if we already have a quick mode task queued for the exchange with the + * given message ID + */ +static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid) +{ + enumerator_t *enumerator; + quick_mode_t *qm; + task_t *task; + bool found = FALSE; + + enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_QUICK_MODE) + { + qm = (quick_mode_t*)task; + if (qm->get_mid(qm) == mid) + { + found = TRUE; + break; + } + } + } + enumerator->destroy(enumerator); + return found; +} + +/** * handle an incoming request message */ static status_t process_request(private_task_manager_t *this, @@ -911,6 +939,7 @@ static status_t process_request(private_task_manager_t *this, bool send_response = FALSE, dpd = FALSE; if (message->get_exchange_type(message) == INFORMATIONAL_V1 || + message->get_exchange_type(message) == QUICK_MODE || this->passive_tasks->get_count(this->passive_tasks) == 0) { /* create tasks depending on request type, if not already some queued */ switch (message->get_exchange_type(message)) @@ -946,6 +975,10 @@ static status_t process_request(private_task_manager_t *this, "unestablished IKE_SA, ignored"); return FAILED; } + if (have_quick_mode_task(this, message->get_message_id(message))) + { + break; + } task = (task_t *)quick_mode_create(this->ike_sa, NULL, NULL, NULL); this->passive_tasks->insert_last(this->passive_tasks, task); diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 96edfd8d8..d6a3f2cd1 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Hochschule fuer Technik Rapperswil * * Copyright (C) 2011 Martin Willi @@ -185,6 +185,11 @@ struct private_quick_mode_t { */ bool udp; + /** + * Message ID of handled quick mode exchange + */ + u_int32_t mid; + /** states of quick mode */ enum { QM_INIT, @@ -1019,6 +1024,11 @@ static void check_for_rekeyed_child(private_quick_mode_t *this) METHOD(task_t, process_r, status_t, private_quick_mode_t *this, message_t *message) { + if (this->mid && this->mid != message->get_message_id(message)) + { /* not responsible for this quick mode exchange */ + return NEED_MORE; + } + switch (this->state) { case QM_INIT: @@ -1188,6 +1198,11 @@ METHOD(task_t, process_r, status_t, METHOD(task_t, build_r, status_t, private_quick_mode_t *this, message_t *message) { + if (this->mid && this->mid != message->get_message_id(message)) + { /* not responsible for this quick mode exchange */ + return NEED_MORE; + } + switch (this->state) { case QM_INIT: @@ -1242,6 +1257,7 @@ METHOD(task_t, build_r, status_t, add_ts(this, message); this->state = QM_NEGOTIATED; + this->mid = message->get_message_id(message); return NEED_MORE; } case QM_NEGOTIATED: @@ -1335,6 +1351,12 @@ METHOD(task_t, get_type, task_type_t, return TASK_QUICK_MODE; } +METHOD(quick_mode_t, get_mid, u_int32_t, + private_quick_mode_t *this) +{ + return this->mid; +} + METHOD(quick_mode_t, use_reqid, void, private_quick_mode_t *this, u_int32_t reqid) { @@ -1368,6 +1390,7 @@ METHOD(task_t, migrate, void, this->ike_sa = ike_sa; this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); this->state = QM_INIT; + this->mid = 0; this->tsi = NULL; this->tsr = NULL; this->proposal = NULL; @@ -1414,6 +1437,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, .migrate = _migrate, .destroy = _destroy, }, + .get_mid = _get_mid, .use_reqid = _use_reqid, .use_marks = _use_marks, .rekey = _rekey, diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h index ee9b64d13..062d63465 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.h +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2011 Martin Willi * Copyright (C) 2011 revosec AG * @@ -38,6 +41,14 @@ struct quick_mode_t { task_t task; /** + * Get the message ID of the quick mode exchange handled by this task as + * responder. + * + * @return message ID, or 0 (not defined yet or as initiator) + */ + u_int32_t (*get_mid)(quick_mode_t *this); + + /** * Use a specific reqid to install this CHILD_SA. * * @param reqid reqid to use diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index f1442096c..91f6187f9 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -448,6 +448,8 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, identification_t *other_id; auth_cfg_t *auth; keymat_v2_t *keymat; + eap_type_t type; + u_int32_t vendor; auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); @@ -478,6 +480,13 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, this->auth_complete = TRUE; auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); + + type = this->method->get_type(this->method, &vendor); + auth->add(auth, AUTH_RULE_EAP_TYPE, type); + if (vendor) + { + auth->add(auth, AUTH_RULE_EAP_VENDOR, vendor); + } return TRUE; } diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 151b49718..2284a484d 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -321,7 +321,7 @@ METHOD(authenticator_t, build, status_t, chunk_t auth_data; status_t status; auth_payload_t *auth_payload; - auth_method_t auth_method; + auth_method_t auth_method = AUTH_NONE; id = this->ike_sa->get_my_id(this->ike_sa); auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index 6fedc8eb5..fce0840e3 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -112,6 +112,7 @@ static bool derive_ike_aead(private_keymat_v2_t *this, u_int16_t alg, case ENCR_AES_GCM_ICV12: case ENCR_AES_GCM_ICV16: /* RFC 4106 */ + case ENCR_CHACHA20_POLY1305: salt_size = 4; break; case ENCR_AES_CCM_ICV8: @@ -527,6 +528,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool, case ENCR_AES_GCM_ICV16: case ENCR_AES_CTR: case ENCR_NULL_AUTH_AES_GMAC: + case ENCR_CHACHA20_POLY1305: enc_size += 4; break; default: diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index e0f930c3c..e08f3dab1 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -145,6 +145,11 @@ struct private_child_create_t { ipcomp_transform_t ipcomp_received; /** + * IPsec protocol + */ + protocol_id_t proto; + + /** * Own allocated SPI */ u_int32_t my_spi; @@ -260,23 +265,23 @@ static bool allocate_spi(private_child_create_t *this) { enumerator_t *enumerator; proposal_t *proposal; - protocol_id_t proto = PROTO_ESP; if (this->initiator) { + this->proto = PROTO_ESP; /* we just get a SPI for the first protocol. TODO: If we ever support * proposal lists with mixed protocols, we'd need multiple SPIs */ if (this->proposals->get_first(this->proposals, (void**)&proposal) == SUCCESS) { - proto = proposal->get_protocol(proposal); + this->proto = proposal->get_protocol(proposal); } } else { - proto = this->proposal->get_protocol(this->proposal); + this->proto = this->proposal->get_protocol(this->proposal); } - this->my_spi = this->child_sa->alloc_spi(this->child_sa, proto); + this->my_spi = this->child_sa->alloc_spi(this->child_sa, this->proto); if (this->my_spi) { if (this->initiator) @@ -1352,20 +1357,16 @@ METHOD(task_t, build_i_delete, status_t, private_child_create_t *this, message_t *message) { message->set_exchange_type(message, INFORMATIONAL); - if (this->child_sa && this->proposal) + if (this->my_spi && this->proto) { - protocol_id_t proto; delete_payload_t *del; - u_int32_t spi; - proto = this->proposal->get_protocol(this->proposal); - spi = this->child_sa->get_spi(this->child_sa, TRUE); - del = delete_payload_create(PLV2_DELETE, proto); - del->add_spi(del, spi); + del = delete_payload_create(PLV2_DELETE, this->proto); + del->add_spi(del, this->my_spi); message->add_payload(message, (payload_t*)del); DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", - protocol_id_names, proto, ntohl(spi)); + protocol_id_names, this->proto, ntohl(this->my_spi)); } return NEED_MORE; } @@ -1375,9 +1376,13 @@ METHOD(task_t, build_i_delete, status_t, */ static status_t delete_failed_sa(private_child_create_t *this) { - this->public.task.build = _build_i_delete; - this->public.task.process = (void*)return_success; - return NEED_MORE; + if (this->my_spi && this->proto) + { + this->public.task.build = _build_i_delete; + this->public.task.process = (void*)return_success; + return NEED_MORE; + } + return SUCCESS; } METHOD(task_t, process_i, status_t, @@ -1596,6 +1601,7 @@ METHOD(task_t, migrate, void, this->tsi = NULL; this->tsr = NULL; this->dh = NULL; + this->nonceg = NULL; this->child_sa = NULL; this->mode = MODE_TUNNEL; this->ipcomp = IPCOMP_NONE; diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index c806e19ca..c7a8a1342 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -170,13 +170,8 @@ METHOD(task_t, build_i, status_t, } config = this->child_sa->get_config(this->child_sa); - /* we just need the rekey notify ... */ - notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY, - this->protocol, REKEY_SA); - notify->set_spi(notify, this->spi); - message->add_payload(message, (payload_t*)notify); - /* ... our CHILD_CREATE task does the hard work for us. */ + /* our CHILD_CREATE task does the hard work for us */ if (!this->child_create) { this->child_create = child_create_create(this->ike_sa, @@ -194,6 +189,14 @@ METHOD(task_t, build_i, status_t, schedule_delayed_rekey(this); return FAILED; } + if (message->get_exchange_type(message) == CREATE_CHILD_SA) + { + /* don't add the notify if the CHILD_CREATE task changed the exchange */ + notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY, + this->protocol, REKEY_SA); + notify->set_spi(notify, this->spi); + message->add_payload(message, (payload_t*)notify); + } this->child_sa->set_state(this->child_sa, CHILD_REKEYING); return NEED_MORE; @@ -334,8 +337,7 @@ METHOD(task_t, process_i, status_t, if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE) { - /* bad DH group while rekeying, try again */ - this->child_create->task.migrate(&this->child_create->task, this->ike_sa); + /* bad DH group while rekeying, retry, or failure requiring deletion */ return NEED_MORE; } if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL) diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c index 1855517ce..eaba04e3a 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c @@ -116,7 +116,6 @@ static void establish_new(private_ike_rekey_t *this) lib->processor->queue_job(lib->processor, job); } this->new_sa = NULL; - /* set threads active IKE_SA after checkin */ charon->bus->set_sa(charon->bus, this->ike_sa); } } @@ -229,9 +228,10 @@ METHOD(task_t, build_r, status_t, if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) { + charon->bus->set_sa(charon->bus, this->ike_sa); return SUCCESS; } - + charon->bus->set_sa(charon->bus, this->ike_sa); this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); /* rekeying successful, delete the IKE_SA using a subtask */ @@ -335,15 +335,13 @@ METHOD(task_t, process_i, status_t, { charon->ike_sa_manager->checkin( charon->ike_sa_manager, this->new_sa); - /* set threads active IKE_SA after checkin */ - charon->bus->set_sa(charon->bus, this->ike_sa); } + charon->bus->set_sa(charon->bus, this->ike_sa); this->new_sa = NULL; establish_new(other); return SUCCESS; } } - /* set threads active IKE_SA after checkin */ charon->bus->set_sa(charon->bus, this->ike_sa); } @@ -372,9 +370,13 @@ METHOD(ike_rekey_t, collide, void, this->collision = other; } -METHOD(task_t, migrate, void, - private_ike_rekey_t *this, ike_sa_t *ike_sa) +/** + * Cleanup the task + */ +static void cleanup(private_ike_rekey_t *this) { + ike_sa_t *cur_sa; + if (this->ike_init) { this->ike_init->task.destroy(&this->ike_init->task); @@ -383,9 +385,16 @@ METHOD(task_t, migrate, void, { this->ike_delete->task.destroy(&this->ike_delete->task); } + cur_sa = charon->bus->get_sa(charon->bus); DESTROY_IF(this->new_sa); + charon->bus->set_sa(charon->bus, cur_sa); DESTROY_IF(this->collision); +} +METHOD(task_t, migrate, void, + private_ike_rekey_t *this, ike_sa_t *ike_sa) +{ + cleanup(this); this->collision = NULL; this->ike_sa = ike_sa; this->new_sa = NULL; @@ -396,16 +405,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_ike_rekey_t *this) { - if (this->ike_init) - { - this->ike_init->task.destroy(&this->ike_init->task); - } - if (this->ike_delete) - { - this->ike_delete->task.destroy(&this->ike_delete->task); - } - DESTROY_IF(this->new_sa); - DESTROY_IF(this->collision); + cleanup(this); free(this); } diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 73e1abbf3..1a984435c 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -18,8 +19,10 @@ #include <hydra.h> #include <daemon.h> #include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> #include <collections/linked_list.h> +#define INSTALL_DISABLED ((u_int)~0) typedef struct private_shunt_manager_t private_shunt_manager_t; @@ -37,6 +40,21 @@ struct private_shunt_manager_t { * Installed shunts, as child_cfg_t */ linked_list_t *shunts; + + /** + * Lock to safely access the list of shunts + */ + rwlock_t *lock; + + /** + * Number of threads currently installing shunts, or INSTALL_DISABLED + */ + u_int installing; + + /** + * Condvar to signal shunt installation + */ + rwlock_condvar_t *condvar; }; /** @@ -117,9 +135,15 @@ METHOD(shunt_manager_t, install, bool, { enumerator_t *enumerator; child_cfg_t *child_cfg; - bool found = FALSE; + bool found = FALSE, success; /* check if not already installed */ + this->lock->write_lock(this->lock); + if (this->installing == INSTALL_DISABLED) + { /* flush() has been called */ + this->lock->unlock(this->lock); + return FALSE; + } enumerator = this->shunts->create_enumerator(this->shunts); while (enumerator->enumerate(enumerator, &child_cfg)) { @@ -130,16 +154,29 @@ METHOD(shunt_manager_t, install, bool, } } enumerator->destroy(enumerator); - if (found) { DBG1(DBG_CFG, "shunt %N policy '%s' already installed", ipsec_mode_names, child->get_mode(child), child->get_name(child)); + this->lock->unlock(this->lock); return TRUE; } this->shunts->insert_last(this->shunts, child->get_ref(child)); + this->installing++; + this->lock->unlock(this->lock); - return install_shunt_policy(child); + success = install_shunt_policy(child); + + this->lock->write_lock(this->lock); + if (!success) + { + this->shunts->remove(this->shunts, child, NULL); + child->destroy(child); + } + this->installing--; + this->condvar->signal(this->condvar); + this->lock->unlock(this->lock); + return success; } /** @@ -215,6 +252,7 @@ METHOD(shunt_manager_t, uninstall, bool, enumerator_t *enumerator; child_cfg_t *child, *found = NULL; + this->lock->write_lock(this->lock); enumerator = this->shunts->create_enumerator(this->shunts); while (enumerator->enumerate(enumerator, &child)) { @@ -226,6 +264,7 @@ METHOD(shunt_manager_t, uninstall, bool, } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); if (!found) { @@ -239,20 +278,37 @@ METHOD(shunt_manager_t, uninstall, bool, METHOD(shunt_manager_t, create_enumerator, enumerator_t*, private_shunt_manager_t *this) { - return this->shunts->create_enumerator(this->shunts); + this->lock->read_lock(this->lock); + return enumerator_create_cleaner( + this->shunts->create_enumerator(this->shunts), + (void*)this->lock->unlock, this->lock); } -METHOD(shunt_manager_t, destroy, void, +METHOD(shunt_manager_t, flush, void, private_shunt_manager_t *this) { child_cfg_t *child; + this->lock->write_lock(this->lock); + while (this->installing) + { + this->condvar->wait(this->condvar, this->lock); + } while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS) { uninstall_shunt_policy(child); child->destroy(child); } - this->shunts->destroy(this->shunts); + this->installing = INSTALL_DISABLED; + this->lock->unlock(this->lock); +} + +METHOD(shunt_manager_t, destroy, void, + private_shunt_manager_t *this) +{ + this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy)); + this->lock->destroy(this->lock); + this->condvar->destroy(this->condvar); free(this); } @@ -268,9 +324,12 @@ shunt_manager_t *shunt_manager_create() .install = _install, .uninstall = _uninstall, .create_enumerator = _create_enumerator, + .flush = _flush, .destroy = _destroy, }, .shunts = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), ); return &this->public; diff --git a/src/libcharon/sa/shunt_manager.h b/src/libcharon/sa/shunt_manager.h index 28a795dc9..c43f5db3d 100644 --- a/src/libcharon/sa/shunt_manager.h +++ b/src/libcharon/sa/shunt_manager.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -56,6 +57,11 @@ struct shunt_manager_t { enumerator_t* (*create_enumerator)(shunt_manager_t *this); /** + * Clear any installed shunt. + */ + void (*flush)(shunt_manager_t *this); + + /** * Destroy a shunt_manager_t. */ void (*destroy)(shunt_manager_t *this); diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index d6ff3c8c5..63505c960 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Tobias Brunner + * Copyright (C) 2011-2015 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -18,9 +18,12 @@ #include <hydra.h> #include <daemon.h> +#include <threading/mutex.h> #include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> #include <collections/linked_list.h> +#define INSTALL_DISABLED ((u_int)~0) typedef struct private_trap_manager_t private_trap_manager_t; typedef struct trap_listener_t trap_listener_t; @@ -67,6 +70,26 @@ struct private_trap_manager_t { trap_listener_t listener; /** + * list of acquires we currently handle + */ + linked_list_t *acquires; + + /** + * mutex for list of acquires + */ + mutex_t *mutex; + + /** + * number of threads currently installing trap policies, or INSTALL_DISABLED + */ + u_int installing; + + /** + * condvar to signal trap policy installation + */ + rwlock_condvar_t *condvar; + + /** * Whether to ignore traffic selectors from acquires */ bool ignore_acquire_ts; @@ -80,23 +103,58 @@ typedef struct { char *name; /** ref to peer_cfg to initiate */ peer_cfg_t *peer_cfg; - /** ref to instanciated CHILD_SA */ + /** ref to instantiated CHILD_SA (i.e the trap policy) */ child_sa_t *child_sa; - /** TRUE if an acquire is pending */ - bool pending; + /** TRUE in case of wildcard Transport Mode SA */ + bool wildcard; +} entry_t; + +/** + * A handled acquire + */ +typedef struct { /** pending IKE_SA connecting upon acquire */ ike_sa_t *ike_sa; -} entry_t; + /** reqid of pending trap policy */ + u_int32_t reqid; + /** destination address (wildcard case) */ + host_t *dst; +} acquire_t; /** * actually uninstall and destroy an installed entry */ -static void destroy_entry(entry_t *entry) +static void destroy_entry(entry_t *this) +{ + this->child_sa->destroy(this->child_sa); + this->peer_cfg->destroy(this->peer_cfg); + free(this->name); + free(this); +} + +/** + * destroy a cached acquire entry + */ +static void destroy_acquire(acquire_t *this) { - entry->child_sa->destroy(entry->child_sa); - entry->peer_cfg->destroy(entry->peer_cfg); - free(entry->name); - free(entry); + DESTROY_IF(this->dst); + free(this); +} + +/** + * match an acquire entry by reqid + */ +static bool acquire_by_reqid(acquire_t *this, u_int32_t *reqid) +{ + return this->reqid == *reqid; +} + +/** + * match an acquire entry by destination address + */ +static bool acquire_by_dst(acquire_t *this, host_t *dst) +{ + return this->dst && this->dst->ip_equals(this->dst, dst); } METHOD(trap_manager_t, install, u_int32_t, @@ -113,32 +171,49 @@ METHOD(trap_manager_t, install, u_int32_t, linked_list_t *proposals; proposal_t *proposal; protocol_id_t proto = PROTO_ESP; + bool wildcard = FALSE; /* try to resolve addresses */ ike_cfg = peer->get_ike_cfg(peer); other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC); - if (!other || other->is_anyaddr(other)) + if (other && other->is_anyaddr(other) && + child->get_mode(child) == MODE_TRANSPORT) + { + /* allow wildcard for Transport Mode SAs */ + me = host_create_any(other->get_family(other)); + wildcard = TRUE; + } + else if (!other || other->is_anyaddr(other)) { DESTROY_IF(other); DBG1(DBG_CFG, "installing trap failed, remote address unknown"); return 0; } - me = ike_cfg->resolve_me(ike_cfg, other->get_family(other)); - if (!me || me->is_anyaddr(me)) + else { - DESTROY_IF(me); - me = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, other, NULL); - if (!me) + me = ike_cfg->resolve_me(ike_cfg, other->get_family(other)); + if (!me || me->is_anyaddr(me)) { - DBG1(DBG_CFG, "installing trap failed, local address unknown"); - other->destroy(other); - return 0; + DESTROY_IF(me); + me = hydra->kernel_interface->get_source_addr( + hydra->kernel_interface, other, NULL); + if (!me) + { + DBG1(DBG_CFG, "installing trap failed, local address unknown"); + other->destroy(other); + return 0; + } + me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } - me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } this->lock->write_lock(this->lock); + if (this->installing == INSTALL_DISABLED) + { /* flush() has been called */ + this->lock->unlock(this->lock); + me->destroy(me); + return 0; + } enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { @@ -160,6 +235,7 @@ METHOD(trap_manager_t, install, u_int32_t, { DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name); this->lock->unlock(this->lock); + me->destroy(me); return 0; } /* config might have changed so update everything */ @@ -170,8 +246,10 @@ METHOD(trap_manager_t, install, u_int32_t, INIT(entry, .name = strdup(child->get_name(child)), .peer_cfg = peer->get_ref(peer), + .wildcard = wildcard, ); this->traps->insert_first(this->traps, entry); + this->installing++; /* don't hold lock while creating CHILD_SA and installing policies */ this->lock->unlock(this->lock); @@ -220,6 +298,11 @@ METHOD(trap_manager_t, install, u_int32_t, { destroy_entry(found); } + this->lock->write_lock(this->lock); + /* do this at the end, so entries created temporarily are also destroyed */ + this->installing--; + this->condvar->signal(this->condvar); + this->lock->unlock(this->lock); return reqid; } @@ -314,9 +397,12 @@ METHOD(trap_manager_t, acquire, void, { enumerator_t *enumerator; entry_t *entry, *found = NULL; + acquire_t *acquire; peer_cfg_t *peer; child_cfg_t *child; ike_sa_t *ike_sa; + host_t *host; + bool wildcard, ignore = FALSE; this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); @@ -333,11 +419,52 @@ METHOD(trap_manager_t, acquire, void, if (!found) { - DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid); + DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid); this->lock->unlock(this->lock); return; } - if (!cas_bool(&found->pending, FALSE, TRUE)) + reqid = found->child_sa->get_reqid(found->child_sa); + wildcard = found->wildcard; + + this->mutex->lock(this->mutex); + if (wildcard) + { /* for wildcard acquires we check that we don't have a pending acquire + * with the same peer */ + u_int8_t mask; + + dst->to_subnet(dst, &host, &mask); + if (this->acquires->find_first(this->acquires, (void*)acquire_by_dst, + (void**)&acquire, host) == SUCCESS) + { + host->destroy(host); + ignore = TRUE; + } + else + { + INIT(acquire, + .dst = host, + .reqid = reqid, + ); + this->acquires->insert_last(this->acquires, acquire); + } + } + else + { + if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid, + (void**)&acquire, &reqid) == SUCCESS) + { + ignore = TRUE; + } + else + { + INIT(acquire, + .reqid = reqid, + ); + this->acquires->insert_last(this->acquires, acquire); + } + } + this->mutex->unlock(this->mutex); + if (ignore) { DBG1(DBG_CFG, "ignoring acquire, connection attempt pending"); this->lock->unlock(this->lock); @@ -346,12 +473,40 @@ METHOD(trap_manager_t, acquire, void, peer = found->peer_cfg->get_ref(found->peer_cfg); child = found->child_sa->get_config(found->child_sa); child = child->get_ref(child); - reqid = found->child_sa->get_reqid(found->child_sa); /* don't hold the lock while checking out the IKE_SA */ this->lock->unlock(this->lock); - ike_sa = charon->ike_sa_manager->checkout_by_config( + if (wildcard) + { /* the peer config would match IKE_SAs with other peers */ + ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + peer->get_ike_version(peer), TRUE); + if (ike_sa) + { + ike_cfg_t *ike_cfg; + u_int16_t port; + u_int8_t mask; + + ike_sa->set_peer_cfg(ike_sa, peer); + ike_cfg = ike_sa->get_ike_cfg(ike_sa); + + port = ike_cfg->get_other_port(ike_cfg); + dst->to_subnet(dst, &host, &mask); + host->set_port(host, port); + ike_sa->set_other_host(ike_sa, host); + + port = ike_cfg->get_my_port(ike_cfg); + src->to_subnet(src, &host, &mask); + host->set_port(host, port); + ike_sa->set_my_host(ike_sa, host); + + charon->bus->set_sa(charon->bus, ike_sa); + } + } + else + { + ike_sa = charon->ike_sa_manager->checkout_by_config( charon->ike_sa_manager, peer); + } if (ike_sa) { if (ike_sa->get_peer_cfg(ike_sa) == NULL) @@ -363,24 +518,29 @@ METHOD(trap_manager_t, acquire, void, * have a single TS that we can establish in a Quick Mode. */ src = dst = NULL; } + + this->mutex->lock(this->mutex); + acquire->ike_sa = ike_sa; + this->mutex->unlock(this->mutex); + if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) { - /* make sure the entry is still there */ - this->lock->read_lock(this->lock); - if (this->traps->find_first(this->traps, NULL, - (void**)&found) == SUCCESS) - { - found->ike_sa = ike_sa; - } - this->lock->unlock(this->lock); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } else { - ike_sa->destroy(ike_sa); - charon->bus->set_sa(charon->bus, NULL); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); } } + else + { + this->mutex->lock(this->mutex); + this->acquires->remove(this->acquires, acquire, NULL); + this->mutex->unlock(this->mutex); + destroy_acquire(acquire); + child->destroy(child); + } peer->destroy(peer); } @@ -391,26 +551,33 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa) { enumerator_t *enumerator; - entry_t *entry; + acquire_t *acquire; - this->lock->read_lock(this->lock); - enumerator = this->traps->create_enumerator(this->traps); - while (enumerator->enumerate(enumerator, &entry)) + this->mutex->lock(this->mutex); + enumerator = this->acquires->create_enumerator(this->acquires); + while (enumerator->enumerate(enumerator, &acquire)) { - if (entry->ike_sa != ike_sa) + if (!acquire->ike_sa || acquire->ike_sa != ike_sa) { continue; } - if (child_sa && child_sa->get_reqid(child_sa) != - entry->child_sa->get_reqid(entry->child_sa)) + if (child_sa) { - continue; + if (acquire->dst) + { + /* since every wildcard acquire results in a separate IKE_SA + * there is no need to compare the destination address */ + } + else if (child_sa->get_reqid(child_sa) != acquire->reqid) + { + continue; + } } - entry->ike_sa = NULL; - entry->pending = FALSE; + this->acquires->remove_at(this->acquires, enumerator); + destroy_acquire(acquire); } enumerator->destroy(enumerator); - this->lock->unlock(this->lock); + this->mutex->unlock(this->mutex); } METHOD(listener_t, ike_state_change, bool, @@ -444,14 +611,15 @@ METHOD(listener_t, child_state_change, bool, METHOD(trap_manager_t, flush, void, private_trap_manager_t *this) { - linked_list_t *traps; - /* since destroying the CHILD_SA results in events which require a read - * lock we cannot destroy the list while holding the write lock */ this->lock->write_lock(this->lock); - traps = this->traps; + while (this->installing) + { + this->condvar->wait(this->condvar, this->lock); + } + this->traps->destroy_function(this->traps, (void*)destroy_entry); this->traps = linked_list_create(); + this->installing = INSTALL_DISABLED; this->lock->unlock(this->lock); - traps->destroy_function(traps, (void*)destroy_entry); } METHOD(trap_manager_t, destroy, void, @@ -459,6 +627,9 @@ METHOD(trap_manager_t, destroy, void, { charon->bus->remove_listener(charon->bus, &this->listener.listener); this->traps->destroy_function(this->traps, (void*)destroy_entry); + this->acquires->destroy_function(this->acquires, (void*)destroy_acquire); + this->condvar->destroy(this->condvar); + this->mutex->destroy(this->mutex); this->lock->destroy(this->lock); free(this); } @@ -488,7 +659,10 @@ trap_manager_t *trap_manager_create(void) }, }, .traps = linked_list_create(), + .acquires = linked_list_create(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), .ignore_acquire_ts = lib->settings->get_bool(lib->settings, "%s.ignore_acquire_ts", FALSE, lib->ns), ); diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index c8be28594..5fd8ca26d 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -3,7 +3,9 @@ TESTS = libcharon_tests check_PROGRAMS = $(TESTS) libcharon_tests_SOURCES = \ + suites/test_ike_cfg.c \ suites/test_mem_pool.c \ + suites/test_message_chapoly.c \ libcharon_tests.h libcharon_tests.c libcharon_tests_CFLAGS = \ @@ -11,6 +13,8 @@ libcharon_tests_CFLAGS = \ -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan/tests \ + -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \ + -DPLUGINS=\""${s_plugins}\"" \ @COVERAGE_CFLAGS@ libcharon_tests_LDFLAGS = @COVERAGE_LDFLAGS@ diff --git a/src/libcharon/tests/Makefile.in b/src/libcharon/tests/Makefile.in index 7f4f4b24e..910aad928 100644 --- a/src/libcharon/tests/Makefile.in +++ b/src/libcharon/tests/Makefile.in @@ -102,7 +102,9 @@ CONFIG_CLEAN_VPATH_FILES = am__EXEEXT_1 = libcharon_tests$(EXEEXT) am__dirstamp = $(am__leading_dot)dirstamp am_libcharon_tests_OBJECTS = \ + suites/libcharon_tests-test_ike_cfg.$(OBJEXT) \ suites/libcharon_tests-test_mem_pool.$(OBJEXT) \ + suites/libcharon_tests-test_message_chapoly.$(OBJEXT) \ libcharon_tests-libcharon_tests.$(OBJEXT) libcharon_tests_OBJECTS = $(am_libcharon_tests_OBJECTS) libcharon_tests_DEPENDENCIES = \ @@ -427,7 +429,9 @@ urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ libcharon_tests_SOURCES = \ + suites/test_ike_cfg.c \ suites/test_mem_pool.c \ + suites/test_message_chapoly.c \ libcharon_tests.h libcharon_tests.c libcharon_tests_CFLAGS = \ @@ -435,6 +439,8 @@ libcharon_tests_CFLAGS = \ -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan/tests \ + -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \ + -DPLUGINS=\""${s_plugins}\"" \ @COVERAGE_CFLAGS@ libcharon_tests_LDFLAGS = @COVERAGE_LDFLAGS@ @@ -493,8 +499,12 @@ suites/$(am__dirstamp): suites/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) suites/$(DEPDIR) @: > suites/$(DEPDIR)/$(am__dirstamp) +suites/libcharon_tests-test_ike_cfg.$(OBJEXT): suites/$(am__dirstamp) \ + suites/$(DEPDIR)/$(am__dirstamp) suites/libcharon_tests-test_mem_pool.$(OBJEXT): \ suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) +suites/libcharon_tests-test_message_chapoly.$(OBJEXT): \ + suites/$(am__dirstamp) suites/$(DEPDIR)/$(am__dirstamp) libcharon_tests$(EXEEXT): $(libcharon_tests_OBJECTS) $(libcharon_tests_DEPENDENCIES) $(EXTRA_libcharon_tests_DEPENDENCIES) @rm -f libcharon_tests$(EXEEXT) @@ -508,7 +518,9 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcharon_tests-libcharon_tests.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_mem_pool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @@ -534,6 +546,20 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +suites/libcharon_tests-test_ike_cfg.o: suites/test_ike_cfg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_ike_cfg.o -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Tpo -c -o suites/libcharon_tests-test_ike_cfg.o `test -f 'suites/test_ike_cfg.c' || echo '$(srcdir)/'`suites/test_ike_cfg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Tpo suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_ike_cfg.c' object='suites/libcharon_tests-test_ike_cfg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_ike_cfg.o `test -f 'suites/test_ike_cfg.c' || echo '$(srcdir)/'`suites/test_ike_cfg.c + +suites/libcharon_tests-test_ike_cfg.obj: suites/test_ike_cfg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_ike_cfg.obj -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Tpo -c -o suites/libcharon_tests-test_ike_cfg.obj `if test -f 'suites/test_ike_cfg.c'; then $(CYGPATH_W) 'suites/test_ike_cfg.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_ike_cfg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Tpo suites/$(DEPDIR)/libcharon_tests-test_ike_cfg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_ike_cfg.c' object='suites/libcharon_tests-test_ike_cfg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_ike_cfg.obj `if test -f 'suites/test_ike_cfg.c'; then $(CYGPATH_W) 'suites/test_ike_cfg.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_ike_cfg.c'; fi` + suites/libcharon_tests-test_mem_pool.o: suites/test_mem_pool.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_mem_pool.o -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_mem_pool.Tpo -c -o suites/libcharon_tests-test_mem_pool.o `test -f 'suites/test_mem_pool.c' || echo '$(srcdir)/'`suites/test_mem_pool.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_mem_pool.Tpo suites/$(DEPDIR)/libcharon_tests-test_mem_pool.Po @@ -548,6 +574,20 @@ suites/libcharon_tests-test_mem_pool.obj: suites/test_mem_pool.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_mem_pool.obj `if test -f 'suites/test_mem_pool.c'; then $(CYGPATH_W) 'suites/test_mem_pool.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_mem_pool.c'; fi` +suites/libcharon_tests-test_message_chapoly.o: suites/test_message_chapoly.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_message_chapoly.o -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Tpo -c -o suites/libcharon_tests-test_message_chapoly.o `test -f 'suites/test_message_chapoly.c' || echo '$(srcdir)/'`suites/test_message_chapoly.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Tpo suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_message_chapoly.c' object='suites/libcharon_tests-test_message_chapoly.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_message_chapoly.o `test -f 'suites/test_message_chapoly.c' || echo '$(srcdir)/'`suites/test_message_chapoly.c + +suites/libcharon_tests-test_message_chapoly.obj: suites/test_message_chapoly.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT suites/libcharon_tests-test_message_chapoly.obj -MD -MP -MF suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Tpo -c -o suites/libcharon_tests-test_message_chapoly.obj `if test -f 'suites/test_message_chapoly.c'; then $(CYGPATH_W) 'suites/test_message_chapoly.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_message_chapoly.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Tpo suites/$(DEPDIR)/libcharon_tests-test_message_chapoly.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='suites/test_message_chapoly.c' object='suites/libcharon_tests-test_message_chapoly.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -c -o suites/libcharon_tests-test_message_chapoly.obj `if test -f 'suites/test_message_chapoly.c'; then $(CYGPATH_W) 'suites/test_message_chapoly.c'; else $(CYGPATH_W) '$(srcdir)/suites/test_message_chapoly.c'; fi` + libcharon_tests-libcharon_tests.o: libcharon_tests.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcharon_tests_CFLAGS) $(CFLAGS) -MT libcharon_tests-libcharon_tests.o -MD -MP -MF $(DEPDIR)/libcharon_tests-libcharon_tests.Tpo -c -o libcharon_tests-libcharon_tests.o `test -f 'libcharon_tests.c' || echo '$(srcdir)/'`libcharon_tests.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcharon_tests-libcharon_tests.Tpo $(DEPDIR)/libcharon_tests-libcharon_tests.Po diff --git a/src/libcharon/tests/libcharon_tests.c b/src/libcharon/tests/libcharon_tests.c index 1ed0f0c95..ec96de711 100644 --- a/src/libcharon/tests/libcharon_tests.c +++ b/src/libcharon/tests/libcharon_tests.c @@ -27,8 +27,8 @@ static test_configuration_t tests[] = { #define TEST_SUITE(x) \ { .suite = x, }, -#define TEST_SUITE_DEPEND(x, type, args) \ - { .suite = x, .feature = PLUGIN_DEPENDS(type, args) }, +#define TEST_SUITE_DEPEND(x, type, ...) \ + { .suite = x, .feature = PLUGIN_DEPENDS(type, __VA_ARGS__) }, #include "libcharon_tests.h" { .suite = NULL, } }; @@ -37,13 +37,27 @@ static bool test_runner_init(bool init) { if (init) { + char *plugins, *plugindir; + libhydra_init(); libcharon_init(); + + plugins = getenv("TESTS_PLUGINS") ?: + lib->settings->get_str(lib->settings, + "tests.load", PLUGINS); + plugindir = lib->settings->get_str(lib->settings, + "tests.plugindir", PLUGINDIR); + plugin_loader_add_plugindirs(plugindir, plugins); + if (!lib->plugins->load(lib->plugins, plugins)) + { + return FALSE; + } } else { lib->processor->set_threads(lib->processor, 0); lib->processor->cancel(lib->processor); + lib->plugins->unload(lib->plugins); libcharon_deinit(); libhydra_deinit(); } diff --git a/src/libcharon/tests/libcharon_tests.h b/src/libcharon/tests/libcharon_tests.h index dc9681aeb..fb82baccb 100644 --- a/src/libcharon/tests/libcharon_tests.h +++ b/src/libcharon/tests/libcharon_tests.h @@ -13,4 +13,6 @@ * for more details. */ +TEST_SUITE(ike_cfg_suite_create) TEST_SUITE(mem_pool_suite_create) +TEST_SUITE_DEPEND(message_chapoly_suite_create, AEAD, ENCR_CHACHA20_POLY1305, 32) diff --git a/src/libcharon/tests/suites/test_ike_cfg.c b/src/libcharon/tests/suites/test_ike_cfg.c new file mode 100644 index 000000000..8062179b9 --- /dev/null +++ b/src/libcharon/tests/suites/test_ike_cfg.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "test_suite.h" + +#include <config/ike_cfg.h> + +static void assert_family(int expected, char *addr, bool local) +{ + ike_cfg_t *cfg; + int family; + + cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local ? addr : "%any", 500, + local ? "%any" : addr, 500, FRAGMENTATION_NO, 0); + family = ike_cfg_get_family(cfg, local); + ck_assert_msg(expected == family, "expected family %d != %d (addr: '%s')", + expected, family, addr); + cfg->destroy(cfg); +} + +START_TEST(test_get_address_family_empty) +{ + assert_family(AF_UNSPEC, "", _i); +} +END_TEST + +START_TEST(test_get_address_family_addr) +{ + assert_family(AF_INET, "192.168.1.1", _i); + assert_family(AF_INET6, "fec::1", _i); +} +END_TEST + +START_TEST(test_get_address_family_multi) +{ + assert_family(AF_INET, "192.168.1.1,192.168.2.2", _i); + assert_family(AF_INET6, "fec::1,fec::2", _i); + + assert_family(AF_UNSPEC, "192.168.1.1,fec::1", _i); + assert_family(AF_UNSPEC, "fec::1,192.168.1.1", _i); +} +END_TEST + +START_TEST(test_get_address_family_any) +{ + assert_family(AF_UNSPEC, "%any", _i); + + assert_family(AF_INET, "%any4", _i); + assert_family(AF_INET, "0.0.0.0", _i); + + assert_family(AF_INET6, "%any6", _i); + assert_family(AF_INET6, "::", _i); + + assert_family(AF_INET, "192.168.1.1,%any", _i); + assert_family(AF_INET, "192.168.1.1,%any4", _i); + assert_family(AF_UNSPEC, "192.168.1.1,%any6", _i); + + assert_family(AF_INET6, "fec::1,%any", _i); + assert_family(AF_UNSPEC, "fec::1,%any4", _i); + assert_family(AF_INET6, "fec::1,%any6", _i); +} +END_TEST + +START_TEST(test_get_address_family_other) +{ + assert_family(AF_INET, "192.168.1.0", _i); + assert_family(AF_UNSPEC, "192.168.1.0/24", _i); + assert_family(AF_UNSPEC, "192.168.1.0-192.168.1.10", _i); + + assert_family(AF_INET, "192.168.1.0/24,192.168.2.1", _i); + assert_family(AF_INET, "192.168.1.0-192.168.1.10,192.168.2.1", _i); + assert_family(AF_INET6, "192.168.1.0/24,fec::1", _i); + assert_family(AF_INET6, "192.168.1.0-192.168.1.10,fec::1", _i); + + assert_family(AF_INET6, "fec::", _i); + assert_family(AF_UNSPEC, "fec::/64", _i); + assert_family(AF_UNSPEC, "fec::1-fec::10", _i); + + assert_family(AF_INET6, "fec::/64,fed::1", _i); + assert_family(AF_INET6, "fec::1-fec::10,fec::1", _i); + assert_family(AF_INET, "fec::/64,192.168.1.1", _i); + assert_family(AF_INET, "fec::1-fec::10,192.168.1.1", _i); + + assert_family(AF_UNSPEC, "strongswan.org", _i); + assert_family(AF_INET, "192.168.1.0,strongswan.org", _i); + assert_family(AF_INET6, "fec::1,strongswan.org", _i); +} +END_TEST + +Suite *ike_cfg_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("ike_cfg"); + + tc = tcase_create("ike_cfg_get_address_family"); + tcase_add_loop_test(tc, test_get_address_family_empty, 0, 2); + tcase_add_loop_test(tc, test_get_address_family_addr, 0, 2); + tcase_add_loop_test(tc, test_get_address_family_multi, 0, 2); + tcase_add_loop_test(tc, test_get_address_family_any, 0, 2); + tcase_add_loop_test(tc, test_get_address_family_other, 0, 2); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libcharon/tests/suites/test_message_chapoly.c b/src/libcharon/tests/suites/test_message_chapoly.c new file mode 100644 index 000000000..e871cf6c2 --- /dev/null +++ b/src/libcharon/tests/suites/test_message_chapoly.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Martin Willi + * Copyright (C) 2015 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "test_suite.h" + +#include <encoding/message.h> + +static aead_t *aead; + +static iv_gen_t *ivgen; + +METHOD(keymat_t, get_version, ike_version_t, + keymat_t *this) +{ + return IKEV2; +} + +METHOD(keymat_t, get_aead, aead_t*, + keymat_t *this, bool in) +{ + return aead; +} + +METHOD(aead_t, get_iv_gen, iv_gen_t*, + aead_t *this) +{ + return ivgen; +} + +METHOD(iv_gen_t, get_iv, bool, + iv_gen_t *this, u_int64_t seq, size_t size, u_int8_t *buffer) +{ + if (size != 8) + { + return FALSE; + } + memcpy(buffer, "\x10\x11\x12\x13\x14\x15\x16\x17", 8); + return TRUE; +} + +METHOD(iv_gen_t, allocate_iv, bool, + iv_gen_t *this, u_int64_t seq, size_t size, chunk_t *chunk) +{ + if (size != 8) + { + return FALSE; + } + *chunk = chunk_alloc(size); + return get_iv(this, seq, chunk->len, chunk->ptr); +} + +/** + * Appendix B draft-ietf-ipsecme-chacha20-poly1305-06 + */ +START_TEST(test_chacha20poly1305) +{ + u_int64_t spii, spir; + ike_sa_id_t *id; + message_t *m; + u_int32_t window = htonl(10); + chunk_t chunk, exp; + keymat_t keymat = { + .get_version = _get_version, + .create_dh = (void*)return_null, + .create_nonce_gen = (void*)return_null, + .get_aead = _get_aead, + }; + + m = message_create(IKEV2, 0); + m->set_exchange_type(m, INFORMATIONAL); + htoun64(&spii, 0xc0c1c2c3c4c5c6c7); + htoun64(&spir, 0xd0d1d2d3d4d5d6d7); + id = ike_sa_id_create(IKEV2, spii, spir, FALSE); + m->set_ike_sa_id(m, id); + id->destroy(id); + m->set_source(m, host_create_from_string("1.2.3.4", 4500)); + m->set_destination(m, host_create_from_string("4.3.2.1", 4500)); + m->set_message_id(m, 9); + m->add_notify(m, TRUE, SET_WINDOW_SIZE, chunk_from_thing(window)); + + aead = lib->crypto->create_aead(lib->crypto, ENCR_CHACHA20_POLY1305, 32, 4); + ck_assert(aead); + ck_assert(aead->set_key(aead, chunk_from_chars( + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, + 0xa0,0xa1,0xa2,0xa3))); + INIT(ivgen, + .get_iv = _get_iv, + .allocate_iv = _allocate_iv, + .destroy = (void*)free, + ); + aead->get_iv_gen = _get_iv_gen, + + ck_assert(m->generate(m, &keymat, NULL) == SUCCESS); + chunk = m->get_packet_data(m); + exp = chunk_from_chars(0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, + 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7, + 0x2e,0x20,0x25,0x00,0x00,0x00,0x00,0x09, + 0x00,0x00,0x00,0x45,0x29,0x00,0x00,0x29, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x61,0x03,0x94,0x70,0x1f,0x8d,0x01,0x7f, + 0x7c,0x12,0x92,0x48,0x89,0x6b,0x71,0xbf, + 0xe2,0x52,0x36,0xef,0xd7,0xcd,0xc6,0x70, + 0x66,0x90,0x63,0x15,0xb2); + ck_assert_msg(chunk_equals(chunk, exp), "got %B\nexp %B", &chunk, &exp); + ivgen->destroy(ivgen); + aead->destroy(aead); + m->destroy(m); +} +END_TEST + +Suite *message_chapoly_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("chapoly"); + + tc = tcase_create("ChaCha20Poly1305 IKEv2 encryption"); + tcase_add_test(tc, test_chacha20poly1305); + suite_add_tcase(s, tc); + + return s; +} |