diff options
Diffstat (limited to 'src')
167 files changed, 6610 insertions, 572 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e4c0374a2..e71f73db3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,10 @@ if USE_LIBTNCCS SUBDIRS += libtnccs endif +if USE_LIBPTTLS + SUBDIRS += libpttls +endif + if USE_IMCV SUBDIRS += libimcv endif diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index eb187496d..b96ab418b 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -501,7 +501,7 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), (char*)address, FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(priv->name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */ @@ -718,4 +718,3 @@ NMStrongswanPlugin *nm_strongswan_plugin_new(nm_creds_t *creds, } return plugin; } - diff --git a/src/conftest/config.c b/src/conftest/config.c index 5c078d21a..7f05e9c72 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -107,7 +107,7 @@ static ike_cfg_t *load_ike_config(private_config_t *this, settings->get_int(settings, "configs.%s.lport", 500, config), settings->get_str(settings, "configs.%s.rhost", "%any", config), FALSE, settings->get_int(settings, "configs.%s.rport", 500, config), - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); token = settings->get_str(settings, "configs.%s.proposal", NULL, config); if (token) { diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index cce5ff0d4..2f13be93c 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -472,7 +472,7 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), this->gateway, FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index b2d6c3128..b64174f8b 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -62,7 +62,7 @@ processing/jobs/start_action_job.c processing/jobs/start_action_job.h \ processing/jobs/roam_job.c processing/jobs/roam_job.h \ processing/jobs/update_sa_job.c processing/jobs/update_sa_job.h \ processing/jobs/inactivity_job.c processing/jobs/inactivity_job.h \ -sa/eap/eap_method.c sa/eap/eap_method.h \ +sa/eap/eap_method.c sa/eap/eap_method.h sa/eap/eap_inner_method.h \ sa/eap/eap_manager.c sa/eap/eap_manager.h \ sa/xauth/xauth_method.c sa/xauth/xauth_method.h \ sa/xauth/xauth_manager.c sa/xauth/xauth_manager.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 5203890ff..bc25dcf21 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -60,7 +60,7 @@ processing/jobs/start_action_job.c processing/jobs/start_action_job.h \ processing/jobs/roam_job.c processing/jobs/roam_job.h \ processing/jobs/update_sa_job.c processing/jobs/update_sa_job.h \ processing/jobs/inactivity_job.c processing/jobs/inactivity_job.h \ -sa/eap/eap_method.c sa/eap/eap_method.h \ +sa/eap/eap_method.c sa/eap/eap_method.h sa/eap/eap_inner_method.h \ sa/eap/eap_manager.c sa/eap/eap_manager.h \ sa/xauth/xauth_method.c sa/xauth/xauth_method.h \ sa/xauth/xauth_manager.c sa/xauth/xauth_manager.h \ @@ -212,6 +212,13 @@ if MONOLITHIC endif endif +if USE_IPSECKEY + SUBDIRS += plugins/ipseckey +if MONOLITHIC + libcharon_la_LIBADD += plugins/ipseckey/libstrongswan-ipseckey.la +endif +endif + if USE_UPDOWN SUBDIRS += plugins/updown if MONOLITHIC diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index 5e5fbba42..54a054e40 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -95,6 +95,11 @@ struct private_ike_cfg_t { fragmentation_t fragmentation; /** + * DSCP value to use on sent IKE packets + */ + u_int8_t dscp; + + /** * List of proposals to use */ linked_list_t *proposals; @@ -156,6 +161,12 @@ METHOD(ike_cfg_t, get_other_port, u_int16_t, return this->other_port; } +METHOD(ike_cfg_t, get_dscp, u_int8_t, + private_ike_cfg_t *this) +{ + return this->dscp; +} + METHOD(ike_cfg_t, add_proposal, void, private_ike_cfg_t *this, proposal_t *proposal) { @@ -312,7 +323,7 @@ METHOD(ike_cfg_t, destroy, void, ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, char *other, bool other_allow_any, u_int16_t other_port, - fragmentation_t fragmentation) + fragmentation_t fragmentation, u_int8_t dscp) { private_ike_cfg_t *this; @@ -326,6 +337,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .get_other_addr = _get_other_addr, .get_my_port = _get_my_port, .get_other_port = _get_other_port, + .get_dscp = _get_dscp, .add_proposal = _add_proposal, .get_proposals = _get_proposals, .select_proposal = _select_proposal, @@ -345,6 +357,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .other_allow_any = other_allow_any, .my_port = my_port, .other_port = other_port, + .dscp = dscp, .proposals = linked_list_create(), ); diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index 5a7fae1e9..719ceb9dd 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -108,6 +108,13 @@ struct ike_cfg_t { u_int16_t (*get_other_port)(ike_cfg_t *this); /** + * Get the DSCP value to use for IKE packets send from connections. + * + * @return DSCP value + */ + u_int8_t (*get_dscp)(ike_cfg_t *this); + + /** * Adds a proposal to the list. * * The first added proposal has the highest priority, the last @@ -205,11 +212,12 @@ struct ike_cfg_t { * @param other_allow_any allow override of remote address by any address * @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. */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, char *other, bool other_allow_any, u_int16_t other_port, - fragmentation_t fragmentation); + fragmentation_t fragmentation, u_int8_t dscp); #endif /** IKE_CFG_H_ @}*/ diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 28fdda735..ca964d749 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -151,7 +151,7 @@ static payload_rule_t ike_sa_init_r_rules[] = { {SECURITY_ASSOCIATION, 1, 1, FALSE, FALSE}, {KEY_EXCHANGE, 1, 1, FALSE, FALSE}, {NONCE, 1, 1, FALSE, FALSE}, - {CERTIFICATE_REQUEST, 0, 1, FALSE, FALSE}, + {CERTIFICATE_REQUEST, 0, MAX_CERTREQ_PAYLOADS, FALSE, FALSE}, {VENDOR_ID, 0, MAX_VID_PAYLOADS, FALSE, FALSE}, }; @@ -181,7 +181,7 @@ static payload_rule_t ike_auth_i_rules[] = { {AUTHENTICATION, 0, 1, TRUE, TRUE}, {ID_INITIATOR, 0, 1, TRUE, FALSE}, {CERTIFICATE, 0, MAX_CERT_PAYLOADS, TRUE, FALSE}, - {CERTIFICATE_REQUEST, 0, 1, TRUE, FALSE}, + {CERTIFICATE_REQUEST, 0, MAX_CERTREQ_PAYLOADS, TRUE, FALSE}, {ID_RESPONDER, 0, 1, TRUE, FALSE}, #ifdef ME {SECURITY_ASSOCIATION, 0, 1, TRUE, FALSE}, diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c index 6af35e5df..9b9bcc3f5 100644 --- a/src/libcharon/plugins/android/android_service.c +++ b/src/libcharon/plugins/android/android_service.c @@ -266,7 +266,8 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); + hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO, + 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, @@ -386,4 +387,3 @@ android_service_t *android_service_create(android_creds_t *creds) return &this->public; } - diff --git a/src/libcharon/plugins/eap_tnc/eap_tnc.c b/src/libcharon/plugins/eap_tnc/eap_tnc.c index ffa1bae39..7363ade1d 100644 --- a/src/libcharon/plugins/eap_tnc/eap_tnc.c +++ b/src/libcharon/plugins/eap_tnc/eap_tnc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -21,6 +21,8 @@ #include <utils/debug.h> #include <daemon.h> +#include <tncifimv.h> + /** * Maximum size of an EAP-TNC message */ @@ -44,15 +46,50 @@ struct private_eap_tnc_t { eap_tnc_t public; /** + * Outer EAP authentication type + */ + eap_type_t auth_type; + + /** * TLS stack, wrapped by EAP helper */ tls_eap_t *tls_eap; + + /** + * TNCCS instance running over EAP-TNC + */ + tnccs_t *tnccs; + }; METHOD(eap_method_t, initiate, status_t, private_eap_tnc_t *this, eap_payload_t **out) { chunk_t data; + u_int32_t auth_type; + + /* Determine TNC Client Authentication Type */ + switch (this->auth_type) + { + case EAP_TLS: + case EAP_TTLS: + case EAP_PEAP: + auth_type = TNC_AUTH_CERT; + break; + case EAP_MD5: + case EAP_MSCHAPV2: + case EAP_GTC: + case EAP_OTP: + auth_type = TNC_AUTH_PASSWORD; + break; + case EAP_SIM: + case EAP_AKA: + auth_type = TNC_AUTH_SIM; + break; + default: + auth_type = TNC_AUTH_UNKNOWN; + } + this->tnccs->set_auth_type(this->tnccs, auth_type); if (this->tls_eap->initiate(this->tls_eap, &data) == NEED_MORE) { @@ -122,6 +159,18 @@ METHOD(eap_method_t, destroy, void, free(this); } +METHOD(eap_inner_method_t, get_auth_type, eap_type_t, + private_eap_tnc_t *this) +{ + return this->auth_type; +} + +METHOD(eap_inner_method_t, set_auth_type, void, + private_eap_tnc_t *this, eap_type_t type) +{ + this->auth_type = type; +} + /** * Generic private constructor */ @@ -132,19 +181,22 @@ static eap_tnc_t *eap_tnc_create(identification_t *server, int max_msg_count; char* protocol; tnccs_type_t type; - tnccs_t *tnccs; INIT(this, .public = { - .eap_method = { - .initiate = _initiate, - .process = _process, - .get_type = _get_type, - .is_mutual = _is_mutual, - .get_msk = _get_msk, - .get_identifier = _get_identifier, - .set_identifier = _set_identifier, - .destroy = _destroy, + .eap_inner_method = { + .eap_method = { + .initiate = _initiate, + .process = _process, + .get_type = _get_type, + .is_mutual = _is_mutual, + .get_msk = _get_msk, + .get_identifier = _get_identifier, + .set_identifier = _set_identifier, + .destroy = _destroy, + }, + .get_auth_type = _get_auth_type, + .set_auth_type = _set_auth_type, }, }, ); @@ -172,10 +224,11 @@ static eap_tnc_t *eap_tnc_create(identification_t *server, free(this); return NULL; } - tnccs = tnc->tnccs->create_instance(tnc->tnccs, type, is_server); - this->tls_eap = tls_eap_create(EAP_TNC, (tls_t*)tnccs, - EAP_TNC_MAX_MESSAGE_LEN, - max_msg_count, FALSE); + this->tnccs = tnc->tnccs->create_instance(tnc->tnccs, type, is_server, + server, peer, TNC_IFT_EAP_1_1); + this->tls_eap = tls_eap_create(EAP_TNC, &this->tnccs->tls, + EAP_TNC_MAX_MESSAGE_LEN, + max_msg_count, FALSE); if (!this->tls_eap) { free(this); diff --git a/src/libcharon/plugins/eap_tnc/eap_tnc.h b/src/libcharon/plugins/eap_tnc/eap_tnc.h index 09abe60fc..8c881f6cf 100644 --- a/src/libcharon/plugins/eap_tnc/eap_tnc.h +++ b/src/libcharon/plugins/eap_tnc/eap_tnc.h @@ -23,7 +23,7 @@ typedef struct eap_tnc_t eap_tnc_t; -#include <sa/eap/eap_method.h> +#include <sa/eap/eap_inner_method.h> /** * Implementation of the eap_method_t interface using EAP-TNC. @@ -31,9 +31,9 @@ typedef struct eap_tnc_t eap_tnc_t; struct eap_tnc_t { /** - * Implemented eap_method_t interface. + * Implemented eap_inner_method_t interface. */ - eap_method_t eap_method; + eap_inner_method_t eap_inner_method; }; /** diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_server.c b/src/libcharon/plugins/eap_ttls/eap_ttls_server.c index 464de17ba..eef8d6682 100644 --- a/src/libcharon/plugins/eap_ttls/eap_ttls_server.c +++ b/src/libcharon/plugins/eap_ttls/eap_ttls_server.c @@ -20,6 +20,7 @@ #include <daemon.h> #include <sa/eap/eap_method.h> +#include <sa/eap/eap_inner_method.h> typedef struct private_eap_ttls_server_t private_eap_ttls_server_t; @@ -108,8 +109,11 @@ static status_t start_phase2_auth(private_eap_ttls_server_t *this) /** * If configured, start EAP-TNC protocol */ -static status_t start_phase2_tnc(private_eap_ttls_server_t *this) +static status_t start_phase2_tnc(private_eap_ttls_server_t *this, + eap_type_t auth_type) { + eap_inner_method_t *inner_method; + if (this->start_phase2_tnc && lib->settings->get_bool(lib->settings, "%s.plugins.eap-ttls.phase2_tnc", FALSE, charon->name)) { @@ -121,6 +125,9 @@ static status_t start_phase2_tnc(private_eap_ttls_server_t *this) DBG1(DBG_IKE, "%N method not available", eap_type_names, EAP_TNC); return FAILED; } + inner_method = (eap_inner_method_t *)this->method; + inner_method->set_auth_type(inner_method, auth_type); + this->start_phase2_tnc = FALSE; if (this->method->initiate(this->method, &this->out) == NEED_MORE) { @@ -237,7 +244,7 @@ METHOD(tls_application_t, process, status_t, if (lib->settings->get_bool(lib->settings, "%s.plugins.eap-ttls.request_peer_auth", FALSE, charon->name)) { - return start_phase2_tnc(this); + return start_phase2_tnc(this, EAP_TLS); } else { @@ -265,7 +272,7 @@ METHOD(tls_application_t, process, status_t, this->method = NULL; /* continue phase2 with EAP-TNC? */ - return start_phase2_tnc(this); + return start_phase2_tnc(this, type); case NEED_MORE: break; case FAILED: diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index 130c86e48..e6a09a76e 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -205,7 +205,7 @@ static void setup_tunnel(private_ha_tunnel_t *this, /* create config and backend */ ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); + remote, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("ha", ike_cfg, CERT_NEVER_SEND, UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, FALSE, 30, @@ -288,4 +288,3 @@ ha_tunnel_t *ha_tunnel_create(char *local, char *remote, char *secret) return &this->public; } - diff --git a/src/libcharon/plugins/ipseckey/Makefile.am b/src/libcharon/plugins/ipseckey/Makefile.am new file mode 100644 index 000000000..0614017a0 --- /dev/null +++ b/src/libcharon/plugins/ipseckey/Makefile.am @@ -0,0 +1,18 @@ + +INCLUDES = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-ipseckey.la +else +plugin_LTLIBRARIES = libstrongswan-ipseckey.la +endif + +libstrongswan_ipseckey_la_SOURCES = \ + ipseckey_plugin.h ipseckey_plugin.c \ + ipseckey_cred.h ipseckey_cred.c \ + ipseckey.h ipseckey.c + +libstrongswan_ipseckey_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/ipseckey/ipseckey.c b/src/libcharon/plugins/ipseckey/ipseckey.c new file mode 100644 index 000000000..78ae2cc2a --- /dev/null +++ b/src/libcharon/plugins/ipseckey/ipseckey.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 "ipseckey.h" + +#include <library.h> +#include <utils/debug.h> +#include <bio/bio_reader.h> + +typedef struct private_ipseckey_t private_ipseckey_t; + +/** +* private data of the ipseckey +*/ +struct private_ipseckey_t { + + /** + * public functions + */ + ipseckey_t public; + + /** + * Precedence + */ + u_int8_t precedence; + + /** + * Gateway type + */ + u_int8_t gateway_type; + + /** + * Algorithm + */ + u_int8_t algorithm; + + /** + * Gateway + */ + chunk_t gateway; + + /** + * Public key + */ + chunk_t public_key; +}; + +METHOD(ipseckey_t, get_precedence, u_int8_t, + private_ipseckey_t *this) +{ + return this->precedence; +} + +METHOD(ipseckey_t, get_gateway_type, ipseckey_gw_type_t, + private_ipseckey_t *this) +{ + return this->gateway_type; +} + +METHOD(ipseckey_t, get_algorithm, ipseckey_algorithm_t, + private_ipseckey_t *this) +{ + return this->algorithm; +} + +METHOD(ipseckey_t, get_gateway, chunk_t, + private_ipseckey_t *this) +{ + return this->gateway; +} + +METHOD(ipseckey_t, get_public_key, chunk_t, + private_ipseckey_t *this) +{ + return this->public_key; +} + +METHOD(ipseckey_t, destroy, void, + private_ipseckey_t *this) +{ + chunk_free(&this->gateway); + chunk_free(&this->public_key); + free(this); +} + +/* + * See header + */ +ipseckey_t *ipseckey_create_frm_rr(rr_t *rr) +{ + private_ipseckey_t *this; + bio_reader_t *reader = NULL; + u_int8_t label; + chunk_t tmp; + + INIT(this, + .public = { + .get_precedence = _get_precedence, + .get_gateway_type = _get_gateway_type, + .get_algorithm = _get_algorithm, + .get_gateway = _get_gateway, + .get_public_key = _get_public_key, + .destroy = _destroy, + }, + ); + + if (rr->get_type(rr) != RR_TYPE_IPSECKEY) + { + DBG1(DBG_CFG, "unable to create an ipseckey out of an RR " + "whose type is not IPSECKEY"); + free(this); + return NULL; + } + + /** Parse the content (RDATA field) of the RR */ + reader = bio_reader_create(rr->get_rdata(rr)); + if (!reader->read_uint8(reader, &this->precedence) || + !reader->read_uint8(reader, &this->gateway_type) || + !reader->read_uint8(reader, &this->algorithm)) + { + DBG1(DBG_CFG, "ipseckey RR has a wrong format"); + reader->destroy(reader); + free(this); + } + + switch (this->gateway_type) + { + case IPSECKEY_GW_TP_NOT_PRESENT: + break; + + case IPSECKEY_GW_TP_IPV4: + if (!reader->read_data(reader, 4, &this->gateway)) + { + DBG1(DBG_CFG, "ipseckey gateway field does not contain an " + "IPv4 address as expected"); + reader->destroy(reader); + free(this); + return NULL; + } + this->gateway = chunk_clone(this->gateway); + break; + + case IPSECKEY_GW_TP_IPV6: + if (!reader->read_data(reader, 16, &this->gateway)) + { + DBG1(DBG_CFG, "ipseckey gateway field does not contain an " + "IPv6 address as expected"); + reader->destroy(reader); + free(this); + return NULL; + } + this->gateway = chunk_clone(this->gateway); + break; + + case IPSECKEY_GW_TP_WR_ENC_DNAME: + /** + * Uncompressed domain name as defined in RFC 1035 chapter 3. + * + * TODO: Currently we ignore wire encoded domain names. + * + */ + while (reader->read_uint8(reader, &label) && + label != 0 && label < 192) + { + if (!reader->read_data(reader, label, &tmp)) + { + DBG1(DBG_CFG, "wrong wire encoded domain name format " + "in ipseckey gateway field"); + reader->destroy(reader); + free(this); + return NULL; + } + } + break; + + default: + DBG1(DBG_CFG, "unable to parse ipseckey gateway field"); + reader->destroy(reader); + free(this); + return NULL; + } + + if (!reader->read_data(reader, reader->remaining(reader), + &this->public_key)) + { + DBG1(DBG_CFG, "failed to read ipseckey public key field"); + reader->destroy(reader); + chunk_free(&this->gateway); + free(this); + return NULL; + } + this->public_key = chunk_clone(this->public_key); + reader->destroy(reader); + return &this->public; +} + diff --git a/src/libcharon/plugins/ipseckey/ipseckey.h b/src/libcharon/plugins/ipseckey/ipseckey.h new file mode 100644 index 000000000..0afc067a5 --- /dev/null +++ b/src/libcharon/plugins/ipseckey/ipseckey.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 ipseckey_cred_i ipseckey + * @{ @ingroup ipseckey + */ + +#ifndef IPSECKEY_H_ +#define IPSECKEY_H_ + +typedef struct ipseckey_t ipseckey_t; +typedef enum ipseckey_algorithm_t ipseckey_algorithm_t; +typedef enum ipseckey_gw_type_t ipseckey_gw_type_t; + +#include <library.h> + +/** + * IPSECKEY gateway types as defined in RFC 4025. + */ +enum ipseckey_gw_type_t { + /** No gateway is present */ + IPSECKEY_GW_TP_NOT_PRESENT = 0, + /** A 4-byte IPv4 address is present */ + IPSECKEY_GW_TP_IPV4 = 1, + /** A 16-byte IPv6 address is present */ + IPSECKEY_GW_TP_IPV6 = 2, + /** A wire-encoded domain name is present */ + IPSECKEY_GW_TP_WR_ENC_DNAME = 3, +}; + +/** + * IPSECKEY algorithms as defined in RFC 4025. + */ +enum ipseckey_algorithm_t { + /** No key present */ + IPSECKEY_ALGORITHM_NONE = 0, + /** DSA key */ + IPSECKEY_ALGORITHM_DSA = 1, + /** RSA key */ + IPSECKEY_ALGORITHM_RSA = 2, +}; + +/** + * An IPSECKEY. + * + * Represents an IPSECKEY as defined in RFC 4025: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | precedence | gateway type | algorithm | gateway | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------+ + + * ~ gateway ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | / + * / public key / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + * + * + * Note: RFC 4025 defines that the algorithm field has a length of 7 bits. + * We use 8 bits instead, because the use of 7 bits is very uncommon + * in internet protocols and might be an error in RFC 4025 + * (also the BIND DNS server uses 8 bits for the algorithm field of the + * IPSECKEY resource records). + * + */ +struct ipseckey_t { + + /** + * Get the precedence of the IPSECKEY. + * + * @return precedence + */ + u_int8_t (*get_precedence)(ipseckey_t *this); + + /** + * Get the type of the gateway. + * + * The "gateway type" determines the format of the gateway field + * of the IPSECKEY. + * + * @return gateway type + */ + ipseckey_gw_type_t (*get_gateway_type)(ipseckey_t *this); + + /** + * Get the algorithm. + * + * The "algorithm" determines the format of the public key field + * of the IPSECKEY. + * + * @return algorithm + */ + ipseckey_algorithm_t (*get_algorithm)(ipseckey_t *this); + + /** + * Get the content of the gateway field as chunk. + * + * The content is in network byte order and its format depends on the + * gateway type. + * + * The data pointed by the chunk is still owned by the IPSECKEY. + * Clone it if necessary. + * + * @return gateway field as chunk + */ + chunk_t (*get_gateway)(ipseckey_t *this); + + /** + * Get the content of the public key field as chunk. + * + * The format of the public key depends on the algorithm type. + * + * The data pointed by the chunk is still owned by the IPSECKEY. + * Clone it if necessary. + * + * @return public key field as chunk + */ + chunk_t (*get_public_key)(ipseckey_t *this); + + /** + * Destroy the IPSECKEY. + */ + void (*destroy) (ipseckey_t *this); +}; + +/** + * Create an ipseckey instance out of a resource record. + * + * @param rr resource record which contains an IPSECKEY + * @return ipseckey, NULL on failure + */ +ipseckey_t *ipseckey_create_frm_rr(rr_t *rr); + +#endif /** IPSECKEY_H_ @}*/ diff --git a/src/libcharon/plugins/ipseckey/ipseckey_cred.c b/src/libcharon/plugins/ipseckey/ipseckey_cred.c new file mode 100644 index 000000000..e8722f12c --- /dev/null +++ b/src/libcharon/plugins/ipseckey/ipseckey_cred.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> + +#include "ipseckey_cred.h" +#include "ipseckey.h" + +#include <bio/bio_reader.h> +#include <daemon.h> + +typedef struct private_ipseckey_cred_t private_ipseckey_cred_t; + +/** + * Private data of an ipseckey_cred_t object + */ +struct private_ipseckey_cred_t { + + /** + * Public part + */ + ipseckey_cred_t public; + + /** + * DNS resolver + */ + resolver_t *res; +}; + +/** + * enumerator over certificates + */ +typedef struct { + /** implements enumerator interface */ + enumerator_t public; + /** inner enumerator (enumerates IPSECKEY resource records) */ + enumerator_t *inner; + /** response of the DNS resolver which contains the IPSECKEYs */ + resolver_response_t *response; + /* IPSECKEYs are not valid before this point in time */ + time_t notBefore; + /* IPSECKEYs are not valid after this point in time */ + time_t notAfter; + /* identity to which the IPSECKEY belongs */ + identification_t *identity; +} cert_enumerator_t; + +METHOD(enumerator_t, cert_enumerator_enumerate, bool, + cert_enumerator_t *this, certificate_t **cert) +{ + rr_t *cur_rr = NULL; + ipseckey_t *cur_ipseckey = NULL; + chunk_t pub_key; + public_key_t * key = NULL; + bool supported_ipseckey_found = FALSE; + + /* Get the next supported IPSECKEY using the inner enumerator. */ + while (this->inner->enumerate(this->inner, &cur_rr) && + !supported_ipseckey_found) + { + supported_ipseckey_found = TRUE; + + cur_ipseckey = ipseckey_create_frm_rr(cur_rr); + + if (!cur_ipseckey) + { + DBG1(DBG_CFG, "failed to parse ipseckey - skipping this key"); + supported_ipseckey_found = FALSE; + } + + if (cur_ipseckey && + cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA) + { + DBG1(DBG_CFG, "unsupported ipseckey algorithm -skipping this key"); + cur_ipseckey->destroy(cur_ipseckey); + supported_ipseckey_found = FALSE; + } + } + + if (supported_ipseckey_found) + { + /* + * Wrap the key of the IPSECKEY in a certificate and return this + * certificate. + */ + pub_key = cur_ipseckey->get_public_key(cur_ipseckey); + + key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, + BUILD_BLOB_DNSKEY, pub_key, + BUILD_END); + + if (!key) + { + DBG1(DBG_CFG, "failed to create public key from ipseckey"); + cur_ipseckey->destroy(cur_ipseckey); + return FALSE; + } + + *cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, + CERT_TRUSTED_PUBKEY, + BUILD_PUBLIC_KEY, key, + BUILD_SUBJECT, this->identity, + BUILD_NOT_BEFORE_TIME, this->notBefore, + BUILD_NOT_AFTER_TIME, this->notAfter, + BUILD_END); + return TRUE; + } + + return FALSE; +} + +METHOD(enumerator_t, cert_enumerator_destroy, void, + cert_enumerator_t *this) +{ + this->inner->destroy(this->inner); + this->response->destroy(this->response); + free(this); +} + +METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, + private_ipseckey_cred_t *this, certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + char *fqdn = NULL; + resolver_response_t *response = NULL; + rr_set_t *rrset = NULL; + enumerator_t *rrsig_enum = NULL; + rr_t *rrsig = NULL; + bio_reader_t *reader = NULL; + chunk_t ignore; + u_int32_t nBefore, nAfter; + cert_enumerator_t *e; + + if (id && id->get_type(id) == ID_FQDN) + { + /** Query the DNS for the required IPSECKEY RRs */ + + if (0 >= asprintf(&fqdn, "%Y", id)) + { + DBG1(DBG_CFG, "empty FQDN string"); + return enumerator_create_empty(); + } + + DBG1(DBG_CFG, "performing a DNS query for IPSECKEY RRs of '%s'", + fqdn); + response = this->res->query(this->res, fqdn, RR_CLASS_IN, + RR_TYPE_IPSECKEY); + if (!response) + { + DBG1(DBG_CFG, " query for IPSECKEY RRs failed"); + free(fqdn); + return enumerator_create_empty(); + } + + if (!response->has_data(response) || + !response->query_name_exist(response)) + { + DBG1(DBG_CFG, " unable to retrieve IPSECKEY RRs from the DNS"); + response->destroy(response); + free(fqdn); + return enumerator_create_empty(); + } + + if (!(response->get_security_state(response) == SECURE)) + { + DBG1(DBG_CFG, " DNSSEC state of IPSECKEY RRs is not secure"); + response->destroy(response); + free(fqdn); + return enumerator_create_empty(); + } + + free(fqdn); + + /** Determine the validity period of the retrieved IPSECKEYs + * + * We use the "Signature Inception" and "Signature Expiration" field + * of the first RRSIG RR to determine the validity period of the + * IPSECKEY RRs. TODO: Take multiple RRSIGs into account. + */ + rrset = response->get_rr_set(response); + rrsig_enum = rrset->create_rrsig_enumerator(rrset); + if (!rrsig_enum || !rrsig_enum->enumerate(rrsig_enum, &rrsig)) + { + DBG1(DBG_CFG, " unable to determine the validity period of " + "IPSECKEY RRs because no RRSIGs are present"); + DESTROY_IF(rrsig_enum); + response->destroy(response); + return enumerator_create_empty(); + } + + /** + * Parse the RRSIG for its validity period (RFC 4034) + */ + reader = bio_reader_create(rrsig->get_rdata(rrsig)); + reader->read_data(reader, 8, &ignore); + reader->read_uint32(reader, &nAfter); + reader->read_uint32(reader, &nBefore); + reader->destroy(reader); + + /*Create and return an iterator over the retrieved IPSECKEYs */ + INIT(e, + .public = { + .enumerate = (void*)_cert_enumerator_enumerate, + .destroy = _cert_enumerator_destroy, + }, + .inner = response->get_rr_set(response)->create_rr_enumerator( + response->get_rr_set(response)), + .response = response, + .notBefore = nBefore, + .notAfter = nAfter, + .identity = id, + ); + + return &e->public; + } + + + return enumerator_create_empty(); +} + +METHOD(ipseckey_cred_t, destroy, void, + private_ipseckey_cred_t *this) +{ + this->res->destroy(this->res); + free(this); +} + +/** + * Described in header. + */ +ipseckey_cred_t *ipseckey_cred_create(resolver_t *res) +{ + private_ipseckey_cred_t *this; + + INIT(this, + .public = { + .set = { + .create_private_enumerator = (void*)return_null, + .create_cert_enumerator = _create_cert_enumerator, + .create_shared_enumerator = (void*)return_null, + .create_cdp_enumerator = (void*)return_null, + .cache_cert = (void*)nop, + }, + .destroy = _destroy, + }, + .res = res, + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/ipseckey/ipseckey_cred.h b/src/libcharon/plugins/ipseckey/ipseckey_cred.h new file mode 100644 index 000000000..440020f5d --- /dev/null +++ b/src/libcharon/plugins/ipseckey/ipseckey_cred.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 ipseckey_cred_i ipseckey_cred + * @{ @ingroup ipseckey + */ + +#ifndef IPSECKEY_CRED_H_ +#define IPSECKEY_CRED_H_ + +#include <credentials/credential_set.h> +#include <resolver/resolver.h> + +typedef struct ipseckey_cred_t ipseckey_cred_t; + +/** + * IPSECKEY credential set. + * + * The ipseckey credential set contains IPSECKEYs as certificates of type + * pubkey_cert_t. + */ +struct ipseckey_cred_t { + + /** + * Implements credential_set_t interface + */ + credential_set_t set; + + /** + * Destroy the ipseckey_cred. + */ + void (*destroy)(ipseckey_cred_t *this); +}; + +/** + * Create an ipseckey_cred instance which uses the given resolver + * to query the DNS for IPSECKEY resource records. + * + * @param res resolver to use + * @return credential set + */ +ipseckey_cred_t *ipseckey_cred_create(resolver_t *res); + +#endif /** IPSECKEY_CRED_H_ @}*/ diff --git a/src/libcharon/plugins/ipseckey/ipseckey_plugin.c b/src/libcharon/plugins/ipseckey/ipseckey_plugin.c new file mode 100644 index 000000000..6f0f10507 --- /dev/null +++ b/src/libcharon/plugins/ipseckey/ipseckey_plugin.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 "ipseckey_plugin.h" + +#include <daemon.h> +#include "ipseckey_cred.h" + +typedef struct private_ipseckey_plugin_t private_ipseckey_plugin_t; + + +/** + * private data of the ipseckey plugin + */ +struct private_ipseckey_plugin_t { + + /** + * implements plugin interface + */ + ipseckey_plugin_t public; + + /** + * DNS resolver instance + */ + resolver_t *res; + + /** + * credential set + */ + ipseckey_cred_t *cred; + + /** + * IPSECKEY based authentication enabled + */ + bool enabled; +}; + +METHOD(plugin_t, get_name, char*, + private_ipseckey_plugin_t *this) +{ + return "ipseckey"; +} + +METHOD(plugin_t, destroy, void, + private_ipseckey_plugin_t *this) +{ + if (this->enabled) + { + lib->credmgr->remove_set(lib->credmgr, &this->cred->set); + } + this->res->destroy(this->res); + DESTROY_IF(this->cred); + free(this); +} + +/* + * see header file + */ +plugin_t *ipseckey_plugin_create() +{ + private_ipseckey_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .destroy = _destroy, + }, + }, + .res = lib->resolver->create(lib->resolver), + .enabled = lib->settings->get_bool(lib->settings, + "charon.plugins.ipseckey.enable", FALSE), + ); + + if (!this->res) + { + DBG1(DBG_CFG, "ipseckey_plugin: Failed to create" + "a DNS resolver instance"); + destroy(this); + return NULL; + } + + if (this->enabled) + { + this->cred = ipseckey_cred_create(this->res); + lib->credmgr->add_set(lib->credmgr, &this->cred->set); + } + + return &this->public.plugin; +} + diff --git a/src/libcharon/plugins/ipseckey/ipseckey_plugin.h b/src/libcharon/plugins/ipseckey/ipseckey_plugin.h new file mode 100644 index 000000000..95acc79dd --- /dev/null +++ b/src/libcharon/plugins/ipseckey/ipseckey_plugin.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 ipseckey ipseckey + * @ingroup cplugins + * + * @defgroup ipseckey_plugin ipseckey_plugin + * @{ @ingroup ipseckey + */ + +#ifndef IPSECKEY_PLUGIN_H_ +#define IPSECKEY_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct ipseckey_plugin_t ipseckey_plugin_t; + +/** + * IPSECKEY plugin + * + * The IPSECKEY plugin registers a credential set for IPSECKEYs. + * + * With this credential set it is possible to authenticate tunnel endpoints + * using IPSECKEY resource records which are retrieved from the DNS in a secure + * way (DNSSEC). + */ +struct ipseckey_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** IPSECKEY_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 1033eb517..52afe1b0a 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -491,7 +491,7 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, this->port + num - 1, remote, FALSE, IKEV2_NATT_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); } else { @@ -499,7 +499,7 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) local, FALSE, charon->socket->get_port(charon->socket, FALSE), remote, FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); } ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal)); peer_cfg = peer_cfg_create("load-test", ike_cfg, diff --git a/src/libcharon/plugins/maemo/maemo_service.c b/src/libcharon/plugins/maemo/maemo_service.c index 806e4cd65..d7539c2da 100644 --- a/src/libcharon/plugins/maemo/maemo_service.c +++ b/src/libcharon/plugins/maemo/maemo_service.c @@ -325,7 +325,8 @@ static gboolean initiate_connection(private_maemo_service_t *this, ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); + hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO, + 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(this->current, ike_cfg, @@ -524,4 +525,3 @@ maemo_service_t *maemo_service_create() return &this->public; } - diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 73e128abe..2bff70307 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -105,7 +105,7 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - address, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); + address, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO, 0); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); med_cfg = peer_cfg_create( "mediation", ike_cfg, @@ -381,7 +381,7 @@ medcli_config_t *medcli_config_create(database_t *db) "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), "0.0.0.0", FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO), + FRAGMENTATION_NO, 0), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index be14380ea..06339220a 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -143,10 +143,9 @@ medsrv_config_t *medsrv_config_create(database_t *db) "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), "0.0.0.0", FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO), + FRAGMENTATION_NO, 0), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); return &this->public; } - diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 51432c960..c0b744a68 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -55,6 +55,9 @@ #ifndef SOL_IPV6 #define SOL_IPV6 IPPROTO_IPV6 #endif +#ifndef IPV6_TCLASS +#define IPV6_TCLASS 67 +#endif /* IPV6_RECVPKTINFO is defined in RFC 3542 which obsoletes RFC 2292 that * previously defined IPV6_PKTINFO */ @@ -113,6 +116,26 @@ struct private_socket_default_socket_t { int ipv6_natt; /** + * DSCP value set on IPv4 socket + */ + u_int8_t dscp4; + + /** + * DSCP value set on IPv4 socket for NAT-T (4500 or natt) + */ + u_int8_t dscp4_natt; + + /** + * DSCP value set on IPv6 socket (500 or port) + */ + u_int8_t dscp6; + + /** + * DSCP value set on IPv6 socket for NAT-T (4500 or natt) + */ + u_int8_t dscp6_natt; + + /** * Maximum packet size to receive */ int max_packet; @@ -310,6 +333,7 @@ METHOD(socket_t, sender, status_t, struct msghdr msg; struct cmsghdr *cmsg; struct iovec iov; + u_int8_t *dscp; src = packet->get_source(packet); dst = packet->get_destination(packet); @@ -322,24 +346,34 @@ METHOD(socket_t, sender, status_t, family = dst->get_family(dst); if (sport == 0 || sport == this->port) { - if (family == AF_INET) - { - skt = this->ipv4; - } - else + switch (family) { - skt = this->ipv6; + case AF_INET: + skt = this->ipv4; + dscp = &this->dscp4; + break; + case AF_INET6: + skt = this->ipv6; + dscp = &this->dscp6; + break; + default: + return FAILED; } } else if (sport == this->natt) { - if (family == AF_INET) - { - skt = this->ipv4_natt; - } - else + switch (family) { - skt = this->ipv6_natt; + case AF_INET: + skt = this->ipv4_natt; + dscp = &this->dscp4_natt; + break; + case AF_INET6: + skt = this->ipv6_natt; + dscp = &this->dscp6_natt; + break; + default: + return FAILED; } } else @@ -348,6 +382,43 @@ METHOD(socket_t, sender, status_t, return FAILED; } + /* setting DSCP values per-packet in a cmsg seems not to be supported + * on Linux. We instead setsockopt() before sending it, this should be + * safe as only a single thread calls send(). */ + if (*dscp != packet->get_dscp(packet)) + { + if (family == AF_INET) + { + u_int8_t ds4; + + ds4 = packet->get_dscp(packet) << 2; + if (setsockopt(skt, SOL_IP, IP_TOS, &ds4, sizeof(ds4)) == 0) + { + *dscp = packet->get_dscp(packet); + } + else + { + DBG1(DBG_NET, "unable to set IP_TOS on socket: %s", + strerror(errno)); + } + } + else + { + u_int ds6; + + ds6 = packet->get_dscp(packet) << 2; + if (setsockopt(skt, SOL_IPV6, IPV6_TCLASS, &ds6, sizeof(ds6)) == 0) + { + *dscp = packet->get_dscp(packet); + } + else + { + DBG1(DBG_NET, "unable to set IPV6_TCLASS on socket: %s", + strerror(errno)); + } + } + } + memset(&msg, 0, sizeof(struct msghdr)); msg.msg_name = dst->get_sockaddr(dst);; msg.msg_namelen = *dst->get_sockaddr_len(dst); @@ -433,22 +504,24 @@ static int open_socket(private_socket_default_socket_t *this, int family, u_int16_t *port) { int on = TRUE; - struct sockaddr_storage addr; + union { + struct sockaddr sockaddr; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } addr; socklen_t addrlen; u_int sol, pktinfo = 0; int skt; memset(&addr, 0, sizeof(addr)); - addr.ss_family = family; + addr.sockaddr.sa_family = family; /* precalculate constants depending on address family */ switch (family) { case AF_INET: - { - struct sockaddr_in *sin = (struct sockaddr_in *)&addr; - htoun32(&sin->sin_addr.s_addr, INADDR_ANY); - htoun16(&sin->sin_port, *port); - addrlen = sizeof(struct sockaddr_in); + addr.sin.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin.sin_port = htons(*port); + addrlen = sizeof(addr.sin); sol = SOL_IP; #ifdef IP_PKTINFO pktinfo = IP_PKTINFO; @@ -456,17 +529,13 @@ static int open_socket(private_socket_default_socket_t *this, pktinfo = IP_RECVDSTADDR; #endif break; - } case AF_INET6: - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; - memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any)); - htoun16(&sin6->sin6_port, *port); - addrlen = sizeof(struct sockaddr_in6); + memcpy(&addr.sin6.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + addr.sin6.sin6_port = htons(*port); + addrlen = sizeof(addr.sin6); sol = SOL_IPV6; pktinfo = IPV6_RECVPKTINFO; break; - } default: return 0; } @@ -485,7 +554,7 @@ static int open_socket(private_socket_default_socket_t *this, } /* bind the socket */ - if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0) + if (bind(skt, &addr.sockaddr, addrlen) < 0) { DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno)); close(skt); @@ -495,7 +564,7 @@ static int open_socket(private_socket_default_socket_t *this, /* retrieve randomly allocated port if needed */ if (*port == 0) { - if (getsockname(skt, (struct sockaddr *)&addr, &addrlen) < 0) + if (getsockname(skt, &addr.sockaddr, &addrlen) < 0) { DBG1(DBG_NET, "unable to determine port: %s", strerror(errno)); close(skt); @@ -504,17 +573,11 @@ static int open_socket(private_socket_default_socket_t *this, switch (family) { case AF_INET: - { - struct sockaddr_in *sin = (struct sockaddr_in *)&addr; - *port = untoh16(&sin->sin_port); + *port = ntohs(addr.sin.sin_port); break; - } case AF_INET6: - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; - *port = untoh16(&sin6->sin6_port); + *port = ntohs(addr.sin6.sin6_port); break; - } } } @@ -642,4 +705,3 @@ socket_default_socket_t *socket_default_socket_create() return &this->public; } - diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index 37bd86671..c3471a078 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -262,7 +262,7 @@ static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e, local, FALSE, charon->socket->get_port(charon->socket, FALSE), remote, FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); add_ike_proposals(this, ike_cfg, id); return ike_cfg; } @@ -620,4 +620,3 @@ sql_config_t *sql_config_create(database_t *db) return &this->public; } - diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index ed10bdb9f..deff25878 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -234,7 +234,8 @@ static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg msg->add_conn.other.address, msg->add_conn.other.allow_any, msg->add_conn.other.ikeport, - msg->add_conn.fragmentation); + msg->add_conn.fragmentation, + msg->add_conn.ikedscp); add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL); return ike_cfg; } diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 2771f0146..e31616cf8 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -516,11 +516,18 @@ static void stroke_loglevel(private_stroke_socket_t *this, DBG1(DBG_CFG, "received stroke: loglevel %d for %s", msg->loglevel.level, msg->loglevel.type); - group = enum_from_name(debug_names, msg->loglevel.type); - if ((int)group < 0) + if (strcaseeq(msg->loglevel.type, "any")) { - fprintf(out, "invalid type (%s)!\n", msg->loglevel.type); - return; + group = DBG_ANY; + } + else + { + group = enum_from_name(debug_names, msg->loglevel.type); + if ((int)group < 0) + { + fprintf(out, "invalid type (%s)!\n", msg->loglevel.type); + return; + } } charon->set_level(charon, group, msg->loglevel.level); } diff --git a/src/libcharon/plugins/tnc_imc/Makefile.am b/src/libcharon/plugins/tnc_imc/Makefile.am index 5e2c30df9..eba280690 100644 --- a/src/libcharon/plugins/tnc_imc/Makefile.am +++ b/src/libcharon/plugins/tnc_imc/Makefile.am @@ -4,7 +4,8 @@ INCLUDES = \ -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libtncif \ - -I$(top_srcdir)/src/libtnccs + -I$(top_srcdir)/src/libtnccs \ + -I$(top_srcdir)/src/libtls AM_CFLAGS = -rdynamic diff --git a/src/libcharon/plugins/tnc_imv/Makefile.am b/src/libcharon/plugins/tnc_imv/Makefile.am index eca3b377b..90b3507ce 100644 --- a/src/libcharon/plugins/tnc_imv/Makefile.am +++ b/src/libcharon/plugins/tnc_imv/Makefile.am @@ -4,7 +4,8 @@ INCLUDES = \ -I$(top_srcdir)/src/libhydra \ -I$(top_srcdir)/src/libcharon \ -I$(top_srcdir)/src/libtncif \ - -I$(top_srcdir)/src/libtnccs + -I$(top_srcdir)/src/libtnccs \ + -I$(top_srcdir)/src/libtls AM_CFLAGS = -rdynamic diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c index 39939d34e..422c28bc9 100644 --- a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c @@ -378,7 +378,10 @@ static void process_eap(private_tnc_pdp_t *this, radius_message_t *request, } this->connections->add(this->connections, nas_id, user_name, peer, method); - method->initiate(method, &out); + if (method->initiate(method, &out) == NEED_MORE) + { + send_response(this, request, code, out, group, msk, source); + } } else { @@ -428,16 +431,16 @@ static void process_eap(private_tnc_pdp_t *this, radius_message_t *request, in->get_identifier(in)); } charon->bus->set_sa(charon->bus, NULL); + send_response(this, request, code, out, group, msk, source); + this->connections->unlock(this->connections); } - send_response(this, request, code, out, group, msk, source); - out->destroy(out); - if (code == RMC_ACCESS_ACCEPT || code == RMC_ACCESS_REJECT) { this->connections->remove(this->connections, nas_id, user_name); } + out->destroy(out); end: free(message.ptr); in->destroy(in); @@ -648,4 +651,3 @@ tnc_pdp_t *tnc_pdp_create(u_int16_t port) return &this->public; } - diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c index 0a960635b..f789c31d2 100644 --- a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c @@ -17,6 +17,15 @@ #include <collections/linked_list.h> #include <utils/debug.h> +#include <threading/rwlock.h> +#include <processing/jobs/callback_job.h> + +#include <daemon.h> + +/** + * Default PDP connection timeout, in s + */ +#define DEFAULT_TIMEOUT 30 typedef struct private_tnc_pdp_connections_t private_tnc_pdp_connections_t; typedef struct entry_t entry_t; @@ -32,9 +41,19 @@ struct private_tnc_pdp_connections_t { tnc_pdp_connections_t public; /** - * List of TNC PEP RADIUS Connections + * TNC PEP RADIUS Connections */ linked_list_t *list; + + /** + * Lock to access PEP connection list + */ + rwlock_t *lock; + + /** + * Connection timeout before we kill non-completed connections, in s + */ + int timeout; }; /** @@ -61,6 +80,11 @@ struct entry_t { * IKE SA used for bus communication */ ike_sa_t *ike_sa; + + /** + * Timestamp this entry has been created + */ + time_t created; }; /** @@ -105,6 +129,35 @@ static void dbg_nas_user(chunk_t nas_id, chunk_t user_name, bool not, char *op) } } +/** + * Check if any connection has timed out + */ +static job_requeue_t check_timeouts(private_tnc_pdp_connections_t *this) +{ + enumerator_t *enumerator; + entry_t *entry; + time_t now; + + now = time_monotonic(NULL); + + this->lock->write_lock(this->lock); + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->created + this->timeout <= now) + { + DBG1(DBG_CFG, "RADIUS connection timed out after %d seconds", + this->timeout); + this->list->remove_at(this->list, enumerator); + free_entry(entry); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return JOB_REQUEUE_NONE; +} + METHOD(tnc_pdp_connections_t, add, void, private_tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name, identification_t *peer, eap_method_t *method) @@ -120,6 +173,7 @@ METHOD(tnc_pdp_connections_t, add, void, ike_sa_id->destroy(ike_sa_id); ike_sa->set_other_id(ike_sa, peer); + this->lock->read_lock(this->lock); enumerator = this->list->create_enumerator(this->list); while (enumerator->enumerate(enumerator, &entry)) { @@ -131,20 +185,33 @@ METHOD(tnc_pdp_connections_t, add, void, DBG1(DBG_CFG, "removed stale RADIUS connection"); entry->method = method; entry->ike_sa = ike_sa; + entry->created = time_monotonic(NULL); break; } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); if (!found) { - entry = malloc_thing(entry_t); - entry->nas_id = chunk_clone(nas_id); - entry->user_name = chunk_clone(user_name); - entry->method = method; - entry->ike_sa = ike_sa; + INIT(entry, + .nas_id = chunk_clone(nas_id), + .user_name = chunk_clone(user_name), + .method = method, + .ike_sa = ike_sa, + .created = time_monotonic(NULL), + ); + this->lock->write_lock(this->lock); this->list->insert_last(this->list, entry); + this->lock->unlock(this->lock); } + + /* schedule timeout checking */ + lib->scheduler->schedule_job_ms(lib->scheduler, + (job_t*)callback_job_create((callback_job_cb_t)check_timeouts, + this, NULL, (callback_job_cancel_t)return_false), + this->timeout * 1000); + dbg_nas_user(nas_id, user_name, FALSE, "created"); } @@ -154,6 +221,7 @@ METHOD(tnc_pdp_connections_t, remove_, void, enumerator_t *enumerator; entry_t *entry; + this->lock->write_lock(this->lock); enumerator = this->list->create_enumerator(this->list); while (enumerator->enumerate(enumerator, &entry)) { @@ -166,6 +234,7 @@ METHOD(tnc_pdp_connections_t, remove_, void, } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); } METHOD(tnc_pdp_connections_t, get_state, eap_method_t*, @@ -176,6 +245,7 @@ METHOD(tnc_pdp_connections_t, get_state, eap_method_t*, entry_t *entry; eap_method_t *found = NULL; + this->lock->read_lock(this->lock); enumerator = this->list->create_enumerator(this->list); while (enumerator->enumerate(enumerator, &entry)) { @@ -187,14 +257,25 @@ METHOD(tnc_pdp_connections_t, get_state, eap_method_t*, } } enumerator->destroy(enumerator); + if (!found) + { + this->lock->unlock(this->lock); + } dbg_nas_user(nas_id, user_name, !found, "found"); return found; } +METHOD(tnc_pdp_connections_t, unlock, void, + private_tnc_pdp_connections_t *this) +{ + this->lock->unlock(this->lock); +} + METHOD(tnc_pdp_connections_t, destroy, void, private_tnc_pdp_connections_t *this) { + this->lock->destroy(this->lock); this->list->destroy_function(this->list, (void*)free_entry); free(this); } @@ -211,11 +292,14 @@ tnc_pdp_connections_t *tnc_pdp_connections_create(void) .add = _add, .remove = _remove_, .get_state = _get_state, + .unlock = _unlock, .destroy = _destroy, }, .list = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .timeout = lib->settings->get_int(lib->settings, + "%s.plugins.tnc-pdp.timeout", DEFAULT_TIMEOUT, charon->name), ); return &this->public; } - diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h index 16492020e..442f29ce9 100644 --- a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h @@ -53,7 +53,10 @@ struct tnc_pdp_connections_t { chunk_t user_name); /** - * Get the EAP method and IKE_SA of a registered TNC PEP RADIUS Connection + * Get the EAP method and IKE_SA of a registered TNC PEP RADIUS Connection. + * + * If this call succeeds, the connection manager is locked. Call unlock + * after using the return objects. * * @param nas_id NAS identifier of Policy Enforcement Point * @param user_name User name of TNC Client @@ -64,6 +67,11 @@ struct tnc_pdp_connections_t { chunk_t user_name, ike_sa_t **ike_sa); /** + * Unlock connections after successfully calling get_state(). + */ + void (*unlock)(tnc_pdp_connections_t *this); + + /** * Destroys a tnc_pdp_connections_t object. */ void (*destroy)(tnc_pdp_connections_t *this); diff --git a/src/libcharon/plugins/tnc_tnccs/Makefile.am b/src/libcharon/plugins/tnc_tnccs/Makefile.am index c7fc02f7c..9ee9e86ad 100644 --- a/src/libcharon/plugins/tnc_tnccs/Makefile.am +++ b/src/libcharon/plugins/tnc_tnccs/Makefile.am @@ -1,6 +1,7 @@ INCLUDES = \ -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libtls \ -I$(top_srcdir)/src/libtncif \ -I$(top_srcdir)/src/libtnccs diff --git a/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c b/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c index 0b623d6ff..6ddda594d 100644 --- a/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c +++ b/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -20,7 +20,13 @@ #include <tnc/imc/imc_manager.h> #include <tnc/imv/imv_manager.h> +#include <tncif_identity.h> + +#include <tls.h> + #include <utils/debug.h> +#include <pen/pen.h> +#include <bio/bio_writer.h> #include <collections/linked_list.h> #include <threading/rwlock.h> @@ -158,7 +164,9 @@ METHOD(tnccs_manager_t, remove_method, void, } METHOD(tnccs_manager_t, create_instance, tnccs_t*, - private_tnc_tnccs_manager_t *this, tnccs_type_t type, bool is_server) + private_tnc_tnccs_manager_t *this, tnccs_type_t type, bool is_server, + identification_t *server, identification_t *peer, + tnc_ift_type_t transport) { enumerator_t *enumerator; tnccs_entry_t *entry; @@ -170,7 +178,7 @@ METHOD(tnccs_manager_t, create_instance, tnccs_t*, { if (type == entry->type) { - protocol = entry->constructor(is_server); + protocol = entry->constructor(is_server, server, peer, transport); if (protocol) { break; @@ -442,6 +450,44 @@ static TNC_Result str_attribute(TNC_UInt32 buffer_len, } } +/** + * Write the value of a TNC identity list into the buffer + */ +static TNC_Result identity_attribute(TNC_UInt32 buffer_len, + TNC_BufferReference buffer, + TNC_UInt32 *value_len, + linked_list_t *list) +{ + bio_writer_t *writer; + enumerator_t *enumerator; + u_int32_t count; + chunk_t value; + tncif_identity_t *tnc_id; + TNC_Result result = TNC_RESULT_INVALID_PARAMETER; + + count = list->get_count(list); + writer = bio_writer_create(4 + TNCIF_IDENTITY_MIN_SIZE * count); + writer->write_uint32(writer, count); + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &tnc_id)) + { + tnc_id->build(tnc_id, writer); + } + enumerator->destroy(enumerator); + + value = writer->get_buf(writer); + *value_len = value.len; + if (buffer && buffer_len >= value.len) + { + memcpy(buffer, value.ptr, value.len); + result = TNC_RESULT_SUCCESS; + } + writer->destroy(writer); + + return result; +} + METHOD(tnccs_manager_t, get_attribute, TNC_Result, private_tnc_tnccs_manager_t *this, bool is_imc, TNC_UInt32 imcv_id, @@ -487,6 +533,7 @@ METHOD(tnccs_manager_t, get_attribute, TNC_Result, /* these attributes are supported */ case TNC_ATTRIBUTEID_PRIMARY_IMV_ID: + case TNC_ATTRIBUTEID_AR_IDENTITIES: attribute_match = TRUE; break; @@ -616,15 +663,111 @@ METHOD(tnccs_manager_t, get_attribute, TNC_Result, version = "1.0"; break; default: - return TNC_RESULT_INVALID_PARAMETER; + return TNC_RESULT_INVALID_PARAMETER; } return str_attribute(buffer_len, buffer, value_len, version); } case TNC_ATTRIBUTEID_IFT_PROTOCOL: - return str_attribute(buffer_len, buffer, value_len, - "IF-T for Tunneled EAP"); + { + char *protocol; + + switch (entry->tnccs->get_transport(entry->tnccs)) + { + case TNC_IFT_EAP_1_0: + case TNC_IFT_EAP_1_1: + case TNC_IFT_EAP_2_0: + protocol = "IF-T for Tunneled EAP"; + break; + case TNC_IFT_TLS_1_0: + case TNC_IFT_TLS_2_0: + protocol = "IF-T for TLS"; + break; + default: + return TNC_RESULT_INVALID_PARAMETER; + } + return str_attribute(buffer_len, buffer, value_len, protocol); + } case TNC_ATTRIBUTEID_IFT_VERSION: - return str_attribute(buffer_len, buffer, value_len, "1.1"); + { + char *version; + + switch (entry->tnccs->get_transport(entry->tnccs)) + { + case TNC_IFT_EAP_1_0: + case TNC_IFT_TLS_1_0: + version = "1.0"; + break; + case TNC_IFT_EAP_1_1: + version = "1.1"; + break; + case TNC_IFT_EAP_2_0: + case TNC_IFT_TLS_2_0: + version = "2.0"; + break; + default: + return TNC_RESULT_INVALID_PARAMETER; + } + return str_attribute(buffer_len, buffer, value_len, version); + } + case TNC_ATTRIBUTEID_AR_IDENTITIES: + { + linked_list_t *list; + identification_t *peer; + tnccs_t *tnccs; + tncif_identity_t *tnc_id; + u_int32_t id_type, subject_type; + TNC_Result result; + + list = linked_list_create(); + tnccs = entry->tnccs; + peer = tnccs->tls.get_peer_id(&tnccs->tls); + if (peer) + { + switch (peer->get_type(peer)) + { + case ID_IPV4_ADDR: + id_type = TNC_ID_IPV4_ADDR; + subject_type = TNC_SUBJECT_MACHINE; + break; + case ID_IPV6_ADDR: + id_type = TNC_ID_IPV6_ADDR; + subject_type = TNC_SUBJECT_MACHINE; + break; + case ID_FQDN: + id_type = TNC_ID_USER_NAME; + subject_type = TNC_SUBJECT_USER; + break; + case ID_RFC822_ADDR: + id_type = TNC_ID_RFC822_ADDR; + subject_type = TNC_SUBJECT_USER; + break; + case ID_DER_ASN1_DN: + id_type = TNC_ID_DER_ASN1_DN; + subject_type = TNC_SUBJECT_USER; + break; + case ID_DER_ASN1_GN: + id_type = TNC_ID_DER_ASN1_GN; + subject_type = TNC_SUBJECT_UNKNOWN; + break; + default: + id_type = TNC_ID_UNKNOWN; + subject_type = TNC_SUBJECT_UNKNOWN; + } + if (id_type != TNC_ID_UNKNOWN) + { + tnc_id = tncif_identity_create( + pen_type_create(PEN_TCG, id_type), + peer->get_encoding(peer), + pen_type_create(PEN_TCG, subject_type), + pen_type_create(PEN_TCG, + tnccs->get_auth_type(tnccs))); + list->insert_last(list, tnc_id); + } + } + result = identity_attribute(buffer_len, buffer, value_len, list); + list->destroy_offset(list, offsetof(tncif_identity_t, destroy)); + return result; + } default: return TNC_RESULT_INVALID_PARAMETER; } diff --git a/src/libcharon/plugins/tnccs_11/tnccs_11.c b/src/libcharon/plugins/tnccs_11/tnccs_11.c index cfc29d6ab..53817c710 100644 --- a/src/libcharon/plugins/tnccs_11/tnccs_11.c +++ b/src/libcharon/plugins/tnccs_11/tnccs_11.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -43,9 +43,9 @@ typedef struct private_tnccs_11_t private_tnccs_11_t; struct private_tnccs_11_t { /** - * Public tls_t interface. + * Public tnccs_t interface. */ - tls_t public; + tnccs_t public; /** * TNCC if TRUE, TNCS if FALSE @@ -53,6 +53,26 @@ struct private_tnccs_11_t { bool is_server; /** + * Server identity + */ + identification_t *server; + + /** + * Client identity + */ + identification_t *peer; + + /** + * Underlying TNC IF-T transport protocol + */ + tnc_ift_type_t transport; + + /** + * Type of TNC client authentication + */ + u_int32_t auth_type; + + /** * Connection ID assigned to this TNCCS connection */ TNC_ConnectionID connection_id; @@ -495,6 +515,18 @@ METHOD(tls_t, is_server, bool, return this->is_server; } +METHOD(tls_t, get_server_id, identification_t*, + private_tnccs_11_t *this) +{ + return this->server; +} + +METHOD(tls_t, get_peer_id, identification_t*, + private_tnccs_11_t *this) +{ + return this->peer; +} + METHOD(tls_t, get_purpose, tls_purpose_t, private_tnccs_11_t *this) { @@ -528,29 +560,69 @@ METHOD(tls_t, destroy, void, { tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id, this->is_server); + this->server->destroy(this->server); + this->peer->destroy(this->peer); this->mutex->destroy(this->mutex); DESTROY_IF(this->batch); free(this); } +METHOD(tnccs_t, get_transport, tnc_ift_type_t, + private_tnccs_11_t *this) +{ + return this->transport; +} + +METHOD(tnccs_t, set_transport, void, + private_tnccs_11_t *this, tnc_ift_type_t transport) +{ + this->transport = transport; +} + +METHOD(tnccs_t, get_auth_type, u_int32_t, + private_tnccs_11_t *this) +{ + return this->auth_type; +} + +METHOD(tnccs_t, set_auth_type, void, + private_tnccs_11_t *this, u_int32_t auth_type) +{ + this->auth_type = auth_type; +} + /** * See header */ -tls_t *tnccs_11_create(bool is_server) +tnccs_t* tnccs_11_create(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport) { private_tnccs_11_t *this; INIT(this, .public = { - .process = _process, - .build = _build, - .is_server = _is_server, - .get_purpose = _get_purpose, - .is_complete = _is_complete, - .get_eap_msk = _get_eap_msk, - .destroy = _destroy, + .tls = { + .process = _process, + .build = _build, + .is_server = _is_server, + .get_server_id = _get_server_id, + .get_peer_id = _get_peer_id, + .get_purpose = _get_purpose, + .is_complete = _is_complete, + .get_eap_msk = _get_eap_msk, + .destroy = _destroy, + }, + .get_transport = _get_transport, + .set_transport = _set_transport, + .get_auth_type = _get_auth_type, + .set_auth_type = _set_auth_type, }, .is_server = is_server, + .server = server->clone(server), + .peer = peer->clone(peer), + .transport = transport, .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .max_msg_len = lib->settings->get_int(lib->settings, "%s.plugins.tnccs-11.max_message_size", 45000, diff --git a/src/libcharon/plugins/tnccs_11/tnccs_11.h b/src/libcharon/plugins/tnccs_11/tnccs_11.h index 7331fc8cd..531ebb611 100644 --- a/src/libcharon/plugins/tnccs_11/tnccs_11.h +++ b/src/libcharon/plugins/tnccs_11/tnccs_11.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -23,14 +23,20 @@ #include <library.h> -#include <tls.h> +#include <tnc/tnccs/tnccs.h> /** * Create an instance of the TNC IF-TNCCS 1.1 protocol handler. * - * @param is_server TRUE to act as TNC Server, FALSE for TNC Client - * @return TNC_IF_TNCCS 1.1 protocol stack + * @param is_server TRUE to act as TNC Server, FALSE for TNC Client + * @param server Server identity + * @param peer Client identity + * @param transport Underlying IF-T transport protocol + * @return TNC_IF_TNCCS 1.1 protocol stack */ -tls_t *tnccs_11_create(bool is_server); +tnccs_t* tnccs_11_create(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport); #endif /** TNCCS_11_H_ @}*/ diff --git a/src/libcharon/plugins/tnccs_11/tnccs_11_plugin.c b/src/libcharon/plugins/tnccs_11/tnccs_11_plugin.c index cd95afb1e..f534af008 100644 --- a/src/libcharon/plugins/tnccs_11/tnccs_11_plugin.c +++ b/src/libcharon/plugins/tnccs_11/tnccs_11_plugin.c @@ -30,8 +30,6 @@ METHOD(plugin_t, get_features, int, static plugin_feature_t f[] = { PLUGIN_CALLBACK(tnccs_method_register, tnccs_11_create), PLUGIN_PROVIDE(CUSTOM, "tnccs-1.1"), - PLUGIN_DEPENDS(EAP_SERVER, EAP_TNC), - PLUGIN_DEPENDS(EAP_PEER, EAP_TNC), PLUGIN_DEPENDS(CUSTOM, "tnccs-manager"), }; *features = f; @@ -61,4 +59,3 @@ plugin_t *tnccs_11_plugin_create() return &this->plugin; } - diff --git a/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c b/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c index 3a2c70f5a..b1f6d1eaa 100644 --- a/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c +++ b/src/libcharon/plugins/tnccs_20/batch/pb_tnc_batch.c @@ -386,6 +386,13 @@ static status_t process_tnc_msg(private_pb_tnc_batch_t *this) } else { + if (msg_type == PB_MSG_EXPERIMENTAL && noskip_flag) + { + DBG1(DBG_TNC, "reject PB-Experimental message with NOSKIP flag set"); + msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF, + PB_ERROR_UNSUPPORTED_MANDATORY_MSG, this->offset); + goto fatal; + } if (pb_tnc_msg_infos[msg_type].has_noskip_flag != TRUE_OR_FALSE && pb_tnc_msg_infos[msg_type].has_noskip_flag != noskip_flag) { diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c b/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c index 2d2c1316b..aa5e9c723 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c +++ b/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c @@ -166,6 +166,7 @@ METHOD(pb_tnc_msg_t, process, status_t, { DBG1(DBG_TNC, "PA Subtype 0x%08x is reserved", PA_RESERVED_SUBTYPE); *offset = 4; + return FAILED; } return SUCCESS; diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.c b/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.c index 63d94b94d..2ef8dd6cd 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.c +++ b/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.c @@ -66,24 +66,24 @@ struct private_pb_remediation_parameters_msg_t { pb_tnc_msg_type_t type; /** - * Remediation Parameters Vendor ID + * Remediation Parameters Type */ - u_int32_t vendor_id; + pen_type_t parameters_type; /** - * Remediation Parameters Type + * Remediation Parameters */ - u_int32_t parameters_type; + chunk_t parameters; /** - * Remediation Parameters string + * Remediation String */ - chunk_t remediation_string; + chunk_t string; /** - * Language code + * Remediation Language Code */ - chunk_t language_code; + chunk_t lang_code; /** * Encoded message @@ -113,10 +113,9 @@ METHOD(pb_tnc_msg_t, build, void, return; } writer = bio_writer_create(64); - writer->write_uint32(writer, this->vendor_id); - writer->write_uint32(writer, this->parameters_type); - writer->write_data32(writer, this->remediation_string); - writer->write_data8 (writer, this->language_code); + writer->write_uint32(writer, this->parameters_type.vendor_id); + writer->write_uint32(writer, this->parameters_type.type); + writer->write_data32(writer, this->parameters); this->encoding = writer->get_buf(writer); this->encoding = chunk_clone(this->encoding); @@ -127,83 +126,103 @@ METHOD(pb_tnc_msg_t, process, status_t, private_pb_remediation_parameters_msg_t *this, u_int32_t *offset) { bio_reader_t *reader; + u_int8_t reserved; + status_t status = SUCCESS; + u_char *pos; + + *offset = 0; /* process message */ reader = bio_reader_create(this->encoding); - reader->read_uint32(reader, &this->vendor_id); - reader->read_uint32(reader, &this->parameters_type); + reader->read_uint8 (reader, &reserved); + reader->read_uint24(reader, &this->parameters_type.vendor_id); + reader->read_uint32(reader, &this->parameters_type.type); + reader->read_data (reader, reader->remaining(reader), &this->parameters); - if (!reader->read_data32(reader, &this->remediation_string)) + this->parameters = chunk_clone(this->parameters); + reader->destroy(reader); + + if (this->parameters_type.vendor_id == PEN_IETF && + this->parameters_type.type == PB_REMEDIATION_STRING) { - DBG1(DBG_TNC, "could not parse remediation string"); - reader->destroy(reader); + reader = bio_reader_create(this->parameters); + status = FAILED; *offset = 8; - return FAILED; - }; - this->remediation_string = chunk_clone(this->remediation_string); - if (this->remediation_string.len && - this->remediation_string.ptr[this->remediation_string.len-1] == '\0') - { - DBG1(DBG_TNC, "remediation string must not be null terminated"); + if (!reader->read_data32(reader, &this->string)) + { + DBG1(DBG_TNC, "insufficient data for remediation string"); + goto end; + }; + *offset += 4; + + pos = memchr(this->string.ptr, '\0', this->string.len); + if (pos) + { + DBG1(DBG_TNC, "nul termination in remediation string"); + *offset += (pos - this->string.ptr); + goto end; + } + *offset += this->string.len; + + if (!reader->read_data8(reader, &this->lang_code)) + { + DBG1(DBG_TNC, "insufficient data for remediation string lang code"); + goto end; + }; + *offset += 1; + + pos = memchr(this->lang_code.ptr, '\0', this->lang_code.len); + + if (pos) + { + DBG1(DBG_TNC, "nul termination in remediation string lang code"); + *offset += (pos - this->lang_code.ptr); + goto end; + } + status = SUCCESS; + +end: reader->destroy(reader); - *offset = 11 + this->remediation_string.len; - return FAILED; } - - if (!reader->read_data8(reader, &this->language_code)) - { - DBG1(DBG_TNC, "could not parse language code"); - reader->destroy(reader); - *offset = 12 + this->remediation_string.len; - return FAILED; - }; - this->language_code = chunk_clone(this->language_code); - reader->destroy(reader); - - if (this->language_code.len && - this->language_code.ptr[this->language_code.len-1] == '\0') - { - DBG1(DBG_TNC, "language code must not be null terminated"); - *offset = 12 + this->remediation_string.len + this->language_code.len; - return FAILED; - } - - return SUCCESS; + return status; } METHOD(pb_tnc_msg_t, destroy, void, private_pb_remediation_parameters_msg_t *this) { free(this->encoding.ptr); - free(this->remediation_string.ptr); - free(this->language_code.ptr); + free(this->parameters.ptr); free(this); } -METHOD(pb_remediation_parameters_msg_t, get_vendor_id, u_int32_t, - private_pb_remediation_parameters_msg_t *this, u_int32_t *type) +METHOD(pb_remediation_parameters_msg_t, get_parameters_type, pen_type_t, + private_pb_remediation_parameters_msg_t *this) { - *type = this->parameters_type; - return this->vendor_id; + return this->parameters_type; } -METHOD(pb_remediation_parameters_msg_t, get_remediation_string, chunk_t, +METHOD(pb_remediation_parameters_msg_t, get_parameters, chunk_t, private_pb_remediation_parameters_msg_t *this) { - return this->remediation_string; + return this->parameters; } -METHOD(pb_remediation_parameters_msg_t, get_language_code, chunk_t, - private_pb_remediation_parameters_msg_t *this) +METHOD(pb_remediation_parameters_msg_t, get_string, chunk_t, + private_pb_remediation_parameters_msg_t *this, chunk_t *lang_code) { - return this->language_code; + if (lang_code) + { + *lang_code = this->lang_code; + } + return this->string; } /** * See header */ -pb_tnc_msg_t *pb_remediation_parameters_msg_create_from_data(chunk_t data) +pb_tnc_msg_t* pb_remediation_parameters_msg_create(pen_type_t parameters_type, + chunk_t parameters) { private_pb_remediation_parameters_msg_t *this; @@ -216,24 +235,56 @@ pb_tnc_msg_t *pb_remediation_parameters_msg_create_from_data(chunk_t data) .process = _process, .destroy = _destroy, }, - .get_vendor_id = _get_vendor_id, - .get_remediation_string = _get_remediation_string, - .get_language_code = _get_language_code, + .get_parameters_type = _get_parameters_type, + .get_parameters = _get_parameters, + .get_uri = _get_parameters, + .get_string = _get_string, }, - .type = PB_MSG_REASON_STRING, - .encoding = chunk_clone(data), + .type = PB_MSG_REMEDIATION_PARAMETERS, + .parameters_type = parameters_type, + .parameters = chunk_clone(parameters), ); return &this->public.pb_interface; } /** + * Described in header. + */ +pb_tnc_msg_t* pb_remediation_parameters_msg_create_from_uri(chunk_t uri) +{ + pen_type_t type = { PEN_IETF, PB_REMEDIATION_URI }; + + return pb_remediation_parameters_msg_create(type, uri); +} + +/** + * Described in header. + */ +pb_tnc_msg_t* pb_remediation_parameters_msg_create_from_string(chunk_t string, + chunk_t lang_code) +{ + pb_tnc_msg_t *msg; + bio_writer_t *writer; + pen_type_t type = { PEN_IETF, PB_REMEDIATION_STRING }; + + /* limit language code to 255 octets */ + lang_code.len = min(255, lang_code.len); + + writer = bio_writer_create(4 + string.len + 1 + lang_code.len); + writer->write_data32(writer, string); + writer->write_data8 (writer, lang_code); + + msg = pb_remediation_parameters_msg_create(type, writer->get_buf(writer)); + writer->destroy(writer); + + return msg; +} + +/** * See header */ -pb_tnc_msg_t* pb_remediation_parameters_msg_create(u_int32_t vendor_id, - u_int32_t type, - chunk_t remediation_string, - chunk_t language_code) +pb_tnc_msg_t *pb_remediation_parameters_msg_create_from_data(chunk_t data) { private_pb_remediation_parameters_msg_t *this; @@ -246,16 +297,15 @@ pb_tnc_msg_t* pb_remediation_parameters_msg_create(u_int32_t vendor_id, .process = _process, .destroy = _destroy, }, - .get_vendor_id = _get_vendor_id, - .get_remediation_string = _get_remediation_string, - .get_language_code = _get_language_code, + .get_parameters_type = _get_parameters_type, + .get_parameters = _get_parameters, + .get_uri = _get_parameters, + .get_string = _get_string, }, - .type = PB_MSG_REASON_STRING, - .vendor_id = vendor_id, - .parameters_type = type, - .remediation_string = chunk_clone(remediation_string), - .language_code = chunk_clone(language_code), + .type = PB_MSG_REMEDIATION_PARAMETERS, + .encoding = chunk_clone(data), ); return &this->public.pb_interface; } + diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.h b/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.h index 258d495ec..f3a1c1009 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.h +++ b/src/libcharon/plugins/tnccs_20/messages/pb_remediation_parameters_msg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2011-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ typedef struct pb_remediation_parameters_msg_t pb_remediation_parameters_msg_t; #include "pb_tnc_msg.h" +#include <pen/pen.h> + /** * PB-TNC Remediation Parameter Types as defined in section 4.8.1 of RFC 5793 */ @@ -50,41 +52,61 @@ struct pb_remediation_parameters_msg_t { pb_tnc_msg_t pb_interface; /** - * Get Remediation Parameters Vendor ID and Type + * Get the Remediation Parameters Type (Vendor ID and Type) * - * @param type Remediation Parameters Type - * @return Remediation Parameters Vendor ID + * @return Remediation Parameters Type */ - u_int32_t (*get_vendor_id)(pb_remediation_parameters_msg_t *this, - u_int32_t *type); + pen_type_t (*get_parameters_type)(pb_remediation_parameters_msg_t *this); /** - * Get Remediation String + * Get the Remediation Parameters * - * @return Remediation String + * @return Remediation Parameters */ - chunk_t (*get_remediation_string)(pb_remediation_parameters_msg_t *this); + chunk_t (*get_parameters)(pb_remediation_parameters_msg_t *this); /** - * Get Reason String Language Code + * Get the Remediation URI * - * @return Language Code + * @return Remediation URI */ - chunk_t (*get_language_code)(pb_remediation_parameters_msg_t *this); + chunk_t (*get_uri)(pb_remediation_parameters_msg_t *this); + + /** + * Get the Remediation String + * + * @param lang_code Optional Language Code + * @return Remediation String + */ + chunk_t (*get_string)(pb_remediation_parameters_msg_t *this, + chunk_t *lang_code); + }; /** - * Create a PB-Remediation-Parameters message from parameters + * Create a general PB-Remediation-Parameters message + * + * @param parameters_type Remediation Parameters Type + * @param parameters Remediation Parameters + */ +pb_tnc_msg_t* pb_remediation_parameters_msg_create(pen_type_t parameters_type, + chunk_t parameters); + +/** + * Create a PB-Remediation-Parameters message of IETF Type Remediation URI + * + * @param uri Remediation URI + */ +pb_tnc_msg_t* pb_remediation_parameters_msg_create_from_uri(chunk_t uri); + +/** + * Create a PB-Remediation-Parameters message of IETF Type Remediation String * - * @param vendor_id Remediation Parameters Vendor ID - * @param type Remediation Parameters Type - * @param remediation_string Remediation String - * @param language_code Language Code + * @param string Remediation String + * @param lang_code Remediation String Language Code */ -pb_tnc_msg_t* pb_remediation_parameters_msg_create(u_int32_t vendor_id, - u_int32_t type, - chunk_t remediation_string, - chunk_t language_code); +pb_tnc_msg_t* pb_remediation_parameters_msg_create_from_string(chunk_t string, + chunk_t lang_code); /** * Create an unprocessed PB-Remediation-Parameters message from raw data diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20.c b/src/libcharon/plugins/tnccs_20/tnccs_20.c index 6239b152d..29a161e69 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20.c +++ b/src/libcharon/plugins/tnccs_20/tnccs_20.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2010 Sansar Choinyanbuu - * Copyright (C) 2010-2012 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -48,9 +48,9 @@ typedef struct private_tnccs_20_t private_tnccs_20_t; struct private_tnccs_20_t { /** - * Public tls_t interface. + * Public tnccs_t interface. */ - tls_t public; + tnccs_t public; /** * TNCC if TRUE, TNCS if FALSE @@ -58,6 +58,26 @@ struct private_tnccs_20_t { bool is_server; /** + * Server identity + */ + identification_t *server; + + /** + * Client identity + */ + identification_t *peer; + + /** + * Underlying TNC IF-T transport protocol + */ + tnc_ift_type_t transport; + + /** + * Type of TNC client authentication + */ + u_int32_t auth_type; + + /** * PB-TNC State Machine */ pb_tnc_state_machine_t *state_machine; @@ -291,7 +311,36 @@ static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg) } case PB_MSG_REMEDIATION_PARAMETERS: { - /* TODO : Remediation parameters message processing */ + pb_remediation_parameters_msg_t *rem_msg; + pen_type_t parameters_type; + chunk_t parameters, string, lang_code; + + rem_msg = (pb_remediation_parameters_msg_t*)msg; + parameters_type = rem_msg->get_parameters_type(rem_msg); + parameters = rem_msg->get_parameters(rem_msg); + + if (parameters_type.vendor_id == PEN_IETF) + { + switch (parameters_type.type) + { + case PB_REMEDIATION_URI: + DBG1(DBG_TNC, "remediation uri: %.*s", + parameters.len, parameters.ptr); + break; + case PB_REMEDIATION_STRING: + string = rem_msg->get_string(rem_msg, &lang_code); + DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s", + lang_code.len, lang_code.ptr, + string.len, string.ptr); + break; + default: + DBG1(DBG_TNC, "remediation parameters: %B", ¶meters); + } + } + else + { + DBG1(DBG_TNC, "remediation parameters: %B", ¶meters); + } break; } case PB_MSG_ERROR: @@ -356,9 +405,12 @@ static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg) lang_msg = (pb_language_preference_msg_t*)msg; lang = lang_msg->get_language_preference(lang_msg); - DBG2(DBG_TNC, "setting language preference to '%.*s'", - (int)lang.len, lang.ptr); - this->recs->set_preferred_language(this->recs, lang); + if (this->recs) + { + DBG2(DBG_TNC, "setting language preference to '%.*s'", + (int)lang.len, lang.ptr); + this->recs->set_preferred_language(this->recs, lang); + } break; } case PB_MSG_REASON_STRING: @@ -759,6 +811,18 @@ METHOD(tls_t, is_server, bool, return this->is_server; } +METHOD(tls_t, get_server_id, identification_t*, + private_tnccs_20_t *this) +{ + return this->server; +} + +METHOD(tls_t, get_peer_id, identification_t*, + private_tnccs_20_t *this) +{ + return this->peer; +} + METHOD(tls_t, get_purpose, tls_purpose_t, private_tnccs_20_t *this) { @@ -792,6 +856,8 @@ METHOD(tls_t, destroy, void, { tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id, this->is_server); + this->server->destroy(this->server); + this->peer->destroy(this->peer); this->state_machine->destroy(this->state_machine); this->mutex->destroy(this->mutex); this->messages->destroy_offset(this->messages, @@ -799,24 +865,62 @@ METHOD(tls_t, destroy, void, free(this); } +METHOD(tnccs_t, get_transport, tnc_ift_type_t, + private_tnccs_20_t *this) +{ + return this->transport; +} + +METHOD(tnccs_t, set_transport, void, + private_tnccs_20_t *this, tnc_ift_type_t transport) +{ + this->transport = transport; +} + +METHOD(tnccs_t, get_auth_type, u_int32_t, + private_tnccs_20_t *this) +{ + return this->auth_type; +} + +METHOD(tnccs_t, set_auth_type, void, + private_tnccs_20_t *this, u_int32_t auth_type) +{ + this->auth_type = auth_type; +} + /** * See header */ -tls_t *tnccs_20_create(bool is_server) +tnccs_t* tnccs_20_create(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport) { private_tnccs_20_t *this; INIT(this, .public = { - .process = _process, - .build = _build, - .is_server = _is_server, - .get_purpose = _get_purpose, - .is_complete = _is_complete, - .get_eap_msk = _get_eap_msk, - .destroy = _destroy, + .tls = { + .process = _process, + .build = _build, + .is_server = _is_server, + .get_server_id = _get_server_id, + .get_peer_id = _get_peer_id, + .get_purpose = _get_purpose, + .is_complete = _is_complete, + .get_eap_msk = _get_eap_msk, + .destroy = _destroy, + }, + .get_transport = _get_transport, + .set_transport = _set_transport, + .get_auth_type = _get_auth_type, + .set_auth_type = _set_auth_type, }, .is_server = is_server, + .server = server->clone(server), + .peer = peer->clone(peer), + .transport = transport, .state_machine = pb_tnc_state_machine_create(is_server), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .messages = linked_list_create(), diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20.h b/src/libcharon/plugins/tnccs_20/tnccs_20.h index 400d1dc12..314935069 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20.h +++ b/src/libcharon/plugins/tnccs_20/tnccs_20.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -23,14 +23,20 @@ #include <library.h> -#include <tls.h> +#include <tnc/tnccs/tnccs.h> /** * Create an instance of the TNC IF-TNCCS 2.0 protocol handler. * - * @param is_server TRUE to act as TNC Server, FALSE for TNC Client - * @return TNC_IF_TNCCS 2.0 protocol stack + * @param is_server TRUE to act as TNC Server, FALSE for TNC Client + * @param server Server identity + * @param peer Client identity + * @param transport Underlying IF-T transport protocol + * @return TNC_IF_TNCCS 2.0 protocol stack */ -tls_t *tnccs_20_create(bool is_server); +tnccs_t* tnccs_20_create(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport); #endif /** TNCCS_20_H_ @}*/ diff --git a/src/libcharon/plugins/tnccs_20/tnccs_20_plugin.c b/src/libcharon/plugins/tnccs_20/tnccs_20_plugin.c index 4f419ecf0..f74306c8c 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20_plugin.c +++ b/src/libcharon/plugins/tnccs_20/tnccs_20_plugin.c @@ -30,8 +30,6 @@ METHOD(plugin_t, get_features, int, static plugin_feature_t f[] = { PLUGIN_CALLBACK(tnccs_method_register, tnccs_20_create), PLUGIN_PROVIDE(CUSTOM, "tnccs-2.0"), - PLUGIN_DEPENDS(EAP_SERVER, EAP_TNC), - PLUGIN_DEPENDS(EAP_PEER, EAP_TNC), PLUGIN_DEPENDS(CUSTOM, "tnccs-manager"), }; *features = f; @@ -61,4 +59,3 @@ plugin_t *tnccs_20_plugin_create() return &this->plugin; } - diff --git a/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.c b/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.c index 03795a947..d4fc6a6f7 100644 --- a/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.c +++ b/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Andreas Steffen + * Copyright (C) 2011-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -27,14 +27,35 @@ typedef struct private_tnccs_dynamic_t private_tnccs_dynamic_t; struct private_tnccs_dynamic_t { /** - * Public tls_t interface. + * Public tnccs_t interface. */ - tls_t public; + tnccs_t public; + + /** + * Server identity + */ + identification_t *server; + + /** + * Client identity + */ + identification_t *peer; /** * Detected TNC IF-TNCCS stack */ tls_t *tls; + + /** + * Underlying TNC IF-T transport protocol + */ + tnc_ift_type_t transport; + + /** + * Type of TNC client authentication + */ + u_int32_t auth_type; + }; /** @@ -66,6 +87,7 @@ METHOD(tls_t, process, status_t, private_tnccs_dynamic_t *this, void *buf, size_t buflen) { tnccs_type_t type; + tnccs_t *tnccs; if (!this->tls) { @@ -76,12 +98,15 @@ METHOD(tls_t, process, status_t, type = determine_tnccs_protocol(*(char*)buf); DBG1(DBG_TNC, "%N protocol detected dynamically", tnccs_type_names, type); - this->tls = (tls_t*)tnc->tnccs->create_instance(tnc->tnccs, type, TRUE); - if (!this->tls) + tnccs = tnc->tnccs->create_instance(tnc->tnccs, type, TRUE, + this->server, this->peer, this->transport); + if (!tnccs) { DBG1(DBG_TNC, "N% protocol not supported", tnccs_type_names, type); return FAILED; } + tnccs->set_auth_type(tnccs, this->auth_type); + this->tls = &tnccs->tls; } return this->tls->process(this->tls, buf, buflen); } @@ -98,6 +123,18 @@ METHOD(tls_t, is_server, bool, return TRUE; } +METHOD(tls_t, get_server_id, identification_t*, + private_tnccs_dynamic_t *this) +{ + return this->server; +} + +METHOD(tls_t, get_peer_id, identification_t*, + private_tnccs_dynamic_t *this) +{ + return this->peer; +} + METHOD(tls_t, get_purpose, tls_purpose_t, private_tnccs_dynamic_t *this) { @@ -120,26 +157,66 @@ METHOD(tls_t, destroy, void, private_tnccs_dynamic_t *this) { DESTROY_IF(this->tls); + this->server->destroy(this->server); + this->peer->destroy(this->peer); free(this); } +METHOD(tnccs_t, get_transport, tnc_ift_type_t, + private_tnccs_dynamic_t *this) +{ + return this->transport; +} + +METHOD(tnccs_t, set_transport, void, + private_tnccs_dynamic_t *this, tnc_ift_type_t transport) +{ + this->transport = transport; +} + +METHOD(tnccs_t, get_auth_type, u_int32_t, + private_tnccs_dynamic_t *this) +{ + return this->auth_type; +} + +METHOD(tnccs_t, set_auth_type, void, + private_tnccs_dynamic_t *this, u_int32_t auth_type) +{ + this->auth_type = auth_type; +} + /** * See header */ -tls_t *tnccs_dynamic_create(bool is_server) +tnccs_t* tnccs_dynamic_create(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport) { private_tnccs_dynamic_t *this; INIT(this, .public = { - .process = _process, - .build = _build, - .is_server = _is_server, - .get_purpose = _get_purpose, - .is_complete = _is_complete, - .get_eap_msk = _get_eap_msk, - .destroy = _destroy, + .tls = { + .process = _process, + .build = _build, + .is_server = _is_server, + .get_server_id = _get_server_id, + .get_peer_id = _get_peer_id, + .get_purpose = _get_purpose, + .is_complete = _is_complete, + .get_eap_msk = _get_eap_msk, + .destroy = _destroy, + }, + .get_transport = _get_transport, + .set_transport = _set_transport, + .get_auth_type = _get_auth_type, + .set_auth_type = _set_auth_type, }, + .server = server->clone(server), + .peer = peer->clone(peer), + .transport = transport, ); return &this->public; diff --git a/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.h b/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.h index 42410b17f..e4cff74b8 100644 --- a/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.h +++ b/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Andreas Steffen + * Copyright (C) 2011-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -23,14 +23,20 @@ #include <library.h> -#include <tls.h> +#include <tnc/tnccs/tnccs.h> /** * Create an instance of a dynamic TNC IF-TNCCS protocol handler. * - * @param is_server TRUE to act as TNC Server, FALSE for TNC Client - * @return dynamic TNC IF-TNCCS protocol stack + * @param is_server TRUE to act as TNC Server, FALSE for TNC Client + * @param server Server identity + * @param peer Client identity + * @param transport Underlying IF-T transport protocol + * @return dynamic TNC IF-TNCCS protocol stack */ -tls_t *tnccs_dynamic_create(bool is_server); +tnccs_t* tnccs_dynamic_create(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport); #endif /** TNCCS_DYNAMIC_H_ @}*/ diff --git a/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic_plugin.c b/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic_plugin.c index 6f581c543..aac57813a 100644 --- a/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic_plugin.c +++ b/src/libcharon/plugins/tnccs_dynamic/tnccs_dynamic_plugin.c @@ -32,8 +32,6 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(CUSTOM, "tnccs-dynamic"), PLUGIN_DEPENDS(CUSTOM, "tnccs-1.1"), PLUGIN_DEPENDS(CUSTOM, "tnccs-2.0"), - PLUGIN_DEPENDS(EAP_SERVER, EAP_TNC), - PLUGIN_DEPENDS(EAP_PEER, EAP_TNC), }; *features = f; return countof(f); @@ -62,4 +60,3 @@ plugin_t *tnccs_dynamic_plugin_create() return &this->plugin; } - diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index b7df65d1c..b58d120c1 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -156,7 +156,7 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), remote_addr, FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); ike_cfg->add_proposal(ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); this->peer_cfg = peer_cfg_create( name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_NO, @@ -255,7 +255,7 @@ METHOD(enumerator_t, ike_enumerator_enumerate, bool, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), remote_addr, FALSE, IKEV2_UDP_PORT, - FRAGMENTATION_NO); + FRAGMENTATION_NO, 0); this->ike_cfg->add_proposal(this->ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); diff --git a/src/libcharon/sa/eap/eap_inner_method.h b/src/libcharon/sa/eap/eap_inner_method.h new file mode 100644 index 000000000..500852965 --- /dev/null +++ b/src/libcharon/sa/eap/eap_inner_method.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 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 eap_inner_method eap_inner_method + * @{ @ingroup eap + */ + +#ifndef EAP_INNER_METHOD_H_ +#define EAP_INNER_METHOD_H_ + +typedef struct eap_inner_method_t eap_inner_method_t; + +#include <library.h> + +#include "eap_method.h" + +/** + * Interface of a weak inner EAP method like EAP-TNC or PT-EAP + * that must be encapsulated in a strong TLS-based EAP method + */ +struct eap_inner_method_t { + + /* + * Public EAP method interface + */ + eap_method_t eap_method; + + /* + * Get type of outer EAP authentication method + * + * @return outer EAP authentication type + */ + eap_type_t (*get_auth_type)(eap_inner_method_t *this); + + /* + * Set type of outer EAP Client/Server authentication + * + * @param type outer EAP authentication type + */ + void (*set_auth_type)(eap_inner_method_t *this, eap_type_t type); + +}; + +#endif /** EAP_INNER_METHOD_H_ @}*/ diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 4029db11d..0157599c1 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -900,7 +900,7 @@ METHOD(ike_sa_t, update_hosts, void, else { /* update our address in any case */ - if (!me->equals(me, this->my_host)) + if (force && !me->equals(me, this->my_host)) { set_my_host(this, me->clone(me)); update = TRUE; @@ -909,7 +909,8 @@ METHOD(ike_sa_t, update_hosts, void, if (!other->equals(other, this->other_host)) { /* update others address if we are NOT NATed */ - if (force || !has_condition(this, COND_NAT_HERE)) + if ((has_condition(this, COND_NAT_THERE) && + !has_condition(this, COND_NAT_HERE)) || force ) { set_other_host(this, other->clone(other)); update = TRUE; @@ -939,14 +940,38 @@ METHOD(ike_sa_t, update_hosts, void, } } +/** + * Set configured DSCP value on packet + */ +static void set_dscp(private_ike_sa_t *this, packet_t *packet) +{ + ike_cfg_t *ike_cfg; + + /* prefer IKE config on peer_cfg, as its selection is more accurate + * then the initial IKE config */ + if (this->peer_cfg) + { + ike_cfg = this->peer_cfg->get_ike_cfg(this->peer_cfg); + } + else + { + ike_cfg = this->ike_cfg; + } + if (ike_cfg) + { + packet->set_dscp(packet, ike_cfg->get_dscp(ike_cfg)); + } +} + METHOD(ike_sa_t, generate_message, status_t, private_ike_sa_t *this, message_t *message, packet_t **packet) { status_t status; if (message->is_encoded(message)) - { /* already done */ + { /* already encoded in task, but set DSCP value */ *packet = message->get_packet(message); + set_dscp(this, *packet); return SUCCESS; } this->stats[STAT_OUTBOUND] = time_monotonic(NULL); @@ -955,6 +980,7 @@ METHOD(ike_sa_t, generate_message, status_t, status = message->generate(message, this->keymat, packet); if (status == SUCCESS) { + set_dscp(this, *packet); charon->bus->message(charon->bus, message, FALSE, FALSE); } return status; @@ -1225,24 +1251,6 @@ METHOD(ike_sa_t, process_message, status_t, { /* do not handle messages in passive state */ return FAILED; } - switch (message->get_exchange_type(message)) - { - case ID_PROT: - case AGGRESSIVE: - case IKE_SA_INIT: - case IKE_AUTH: - if (this->state != IKE_CREATED && - this->state != IKE_CONNECTING && - message->get_first_payload_type(message) != FRAGMENT_V1) - { - DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", - exchange_type_names, message->get_exchange_type(message)); - return FAILED; - } - break; - default: - break; - } if (message->get_major_version(message) != this->version) { DBG1(DBG_IKE, "ignoring %N IKEv%u exchange on %N SA", diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 2ac8c3123..c5e6bf4bd 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -108,9 +108,9 @@ struct entry_t { identification_t *other_id; /** - * message ID currently processing, if any + * message ID or hash of currently processing message, -1 if none */ - u_int32_t message_id; + u_int32_t processing; }; /** @@ -135,23 +135,12 @@ static status_t entry_destroy(entry_t *this) */ static entry_t *entry_create() { - entry_t *this = malloc_thing(entry_t); - - this->waiting_threads = 0; - this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); - - /* we set checkout flag when we really give it out */ - this->checked_out = FALSE; - this->driveout_new_threads = FALSE; - this->driveout_waiting_threads = FALSE; - this->message_id = -1; - this->init_hash = chunk_empty; - this->other = NULL; - this->half_open = FALSE; - this->my_id = NULL; - this->other_id = NULL; - this->ike_sa_id = NULL; - this->ike_sa = NULL; + entry_t *this; + + INIT(this, + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .processing = -1, + ); return this; } @@ -1171,6 +1160,20 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*, return ike_sa; } +/** + * Get the message ID or message hash to detect early retransmissions + */ +static u_int32_t get_message_id_or_hash(message_t *message) +{ + /* Use the message ID, or the message hash in IKEv1 Main/Aggressive mode */ + if (message->get_major_version(message) == IKEV1_MAJOR_VERSION && + message->get_message_id(message) == 0) + { + return chunk_hash(message->get_packet_data(message)); + } + return message->get_message_id(message); +} + METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, private_ike_sa_manager_t* this, message_t *message) { @@ -1246,7 +1249,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, entry->checked_out = TRUE; unlock_single_segment(this, segment); - entry->message_id = message->get_message_id(message); + entry->processing = get_message_id_or_hash(message); entry->init_hash = hash; DBG2(DBG_MGR, "created IKE_SA %s[%u]", @@ -1290,12 +1293,11 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS) { - /* only check out in IKEv2 if we are not already processing it */ - if (message->get_request(message) && - message->get_message_id(message) == entry->message_id) + /* only check out if we are not already processing it. */ + if (entry->processing == get_message_id_or_hash(message)) { DBG1(DBG_MGR, "ignoring request with ID %u, already processing", - entry->message_id); + entry->processing); } else if (wait_for_entry(this, entry, segment)) { @@ -1305,7 +1307,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, entry->checked_out = TRUE; if (message->get_first_payload_type(message) != FRAGMENT_V1) { - entry->message_id = message->get_message_id(message); + entry->processing = get_message_id_or_hash(message); } if (ike_id->get_responder_spi(ike_id) == 0) { @@ -1564,7 +1566,7 @@ METHOD(ike_sa_manager_t, checkin, void, entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); /* signal waiting threads */ entry->checked_out = FALSE; - entry->message_id = -1; + entry->processing = -1; /* check if this SA is half-open */ if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING) { @@ -1782,6 +1784,7 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool, { DBG1(DBG_IKE, "destroying duplicate IKE_SA for peer '%Y', " "received INITIAL_CONTACT", other); + charon->bus->ike_updown(charon->bus, duplicate, FALSE); checkin_and_destroy(this, duplicate); continue; } diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 8a4761d5c..7a84d1cef 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1471,6 +1471,21 @@ METHOD(task_manager_t, process_message, status_t, charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg); return SUCCESS; } + + /* reject Main/Agressive Modes once established */ + if (msg->get_exchange_type(msg) == ID_PROT || + msg->get_exchange_type(msg) == AGGRESSIVE) + { + if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED && + this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING && + msg->get_first_payload_type(msg) != FRAGMENT_V1) + { + DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", + exchange_type_names, msg->get_exchange_type(msg)); + return FAILED; + } + } + if (msg->get_exchange_type(msg) == TRANSACTION && this->active_tasks->get_count(this->active_tasks)) { /* main mode not yet complete, queue XAuth/Mode config tasks */ @@ -2030,4 +2045,3 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) return &this->public; } - diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index ea0117c54..29d8d830e 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1123,6 +1123,18 @@ METHOD(task_manager_t, process_message, status_t, { if (mid == this->responding.mid) { + /* reject initial messages once established */ + if (msg->get_exchange_type(msg) == IKE_SA_INIT || + msg->get_exchange_type(msg) == IKE_AUTH) + { + if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED && + this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) + { + DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", + exchange_type_names, msg->get_exchange_type(msg)); + return FAILED; + } + } if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING || msg->get_exchange_type(msg) != IKE_SA_INIT) diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index eb3972c29..32c0e8c4a 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -18,6 +18,7 @@ #include "child_create.h" #include <daemon.h> +#include <hydra.h> #include <sa/ikev2/keymat_v2.h> #include <crypto/diffie_hellman.h> #include <credentials/certificates/x509.h> @@ -615,6 +616,7 @@ static void build_payloads(private_child_create_t *this, message_t *message) nonce_payload_t *nonce_payload; ke_payload_t *ke_payload; ts_payload_t *ts_payload; + kernel_feature_t features; /* add SA payload */ if (this->initiator) @@ -661,6 +663,13 @@ static void build_payloads(private_child_create_t *this, message_t *message) default: break; } + + features = hydra->kernel_interface->get_features(hydra->kernel_interface); + if (!(features & KERNEL_ESP_V3_TFC)) + { + message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED, + chunk_empty); + } } /** diff --git a/src/libcharon/sa/ikev2/tasks/ike_dpd.c b/src/libcharon/sa/ikev2/tasks/ike_dpd.c index 28ccc2efe..7a33f7938 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_dpd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_dpd.c @@ -37,12 +37,6 @@ METHOD(task_t, return_need_more, status_t, return NEED_MORE; } -METHOD(task_t, return_success, status_t, - private_ike_dpd_t *this, message_t *message) -{ - return SUCCESS; -} - METHOD(task_t, get_type, task_type_t, private_ike_dpd_t *this) { @@ -82,11 +76,11 @@ ike_dpd_t *ike_dpd_create(bool initiator) if (initiator) { this->public.task.build = _return_need_more; - this->public.task.process = _return_success; + this->public.task.process = (void*)return_success; } else { - this->public.task.build = _return_success; + this->public.task.build = (void*)return_success; this->public.task.process = _return_need_more; } diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 8948e0561..b82fd6d3d 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -137,6 +137,22 @@ struct private_kernel_interface_t { bool ifaces_exclude; }; +METHOD(kernel_interface_t, get_features, kernel_feature_t, + private_kernel_interface_t *this) +{ + kernel_feature_t features = 0; + + if (this->ipsec && this->ipsec->get_features) + { + features |= this->ipsec->get_features(this->ipsec); + } + if (this->net && this->net->get_features) + { + features |= this->net->get_features(this->net); + } + return features; +} + METHOD(kernel_interface_t, get_spi, status_t, private_kernel_interface_t *this, host_t *src, host_t *dst, u_int8_t protocol, u_int32_t reqid, u_int32_t *spi) @@ -682,6 +698,7 @@ kernel_interface_t *kernel_interface_create() INIT(this, .public = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, @@ -757,4 +774,3 @@ kernel_interface_t *kernel_interface_create() return &this->public; } - diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h index 8d8ef2e83..8d8d048d0 100644 --- a/src/libhydra/kernel/kernel_interface.h +++ b/src/libhydra/kernel/kernel_interface.h @@ -47,6 +47,7 @@ #define KERNEL_INTERFACE_H_ typedef struct kernel_interface_t kernel_interface_t; +typedef enum kernel_feature_t kernel_feature_t; #include <networking/host.h> #include <crypto/prf_plus.h> @@ -56,6 +57,17 @@ typedef struct kernel_interface_t kernel_interface_t; #include <kernel/kernel_net.h> /** + * Bitfield of optional features a kernel backend supports. + * + * This feature-set is for both, kernel_ipsec_t and kernel_net_t. Each + * backend returns a subset of these features. + */ +enum kernel_feature_t { + /** IPsec can process ESPv3 (RFC 4303) TFC padded packets */ + KERNEL_ESP_V3_TFC = (1<<0), +}; + +/** * Constructor function for ipsec kernel interface */ typedef kernel_ipsec_t* (*kernel_ipsec_constructor_t)(void); @@ -74,6 +86,13 @@ typedef kernel_net_t* (*kernel_net_constructor_t)(void); struct kernel_interface_t { /** + * Get the feature set supported by the net and ipsec kernel backends. + * + * @return ORed feature-set of backends + */ + kernel_feature_t (*get_features)(kernel_interface_t *this); + + /** * Get a SPI from the kernel. * * @param src source address of SA diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h index 1da0805cb..c8afcaffd 100644 --- a/src/libhydra/kernel/kernel_ipsec.h +++ b/src/libhydra/kernel/kernel_ipsec.h @@ -30,6 +30,7 @@ typedef struct kernel_ipsec_t kernel_ipsec_t; #include <ipsec/ipsec_types.h> #include <selectors/traffic_selector.h> #include <plugins/plugin.h> +#include <kernel/kernel_interface.h> /** * Interface to the ipsec subsystem of the kernel. @@ -45,6 +46,13 @@ typedef struct kernel_ipsec_t kernel_ipsec_t; struct kernel_ipsec_t { /** + * Get the feature set supported by this kernel backend. + * + * @return ORed feature-set of backend + */ + kernel_feature_t (*get_features)(kernel_ipsec_t *this); + + /** * Get a SPI from the kernel. * * @param src source address of SA diff --git a/src/libhydra/kernel/kernel_net.h b/src/libhydra/kernel/kernel_net.h index 6a3b2cee7..0d3417f1d 100644 --- a/src/libhydra/kernel/kernel_net.h +++ b/src/libhydra/kernel/kernel_net.h @@ -28,6 +28,7 @@ typedef enum kernel_address_type_t kernel_address_type_t; #include <collections/enumerator.h> #include <networking/host.h> #include <plugins/plugin.h> +#include <kernel/kernel_interface.h> /** * Type of addresses (e.g. when enumerating them) @@ -56,6 +57,13 @@ enum kernel_address_type_t { struct kernel_net_t { /** + * Get the feature set supported by this kernel backend. + * + * @return ORed feature-set of backend + */ + kernel_feature_t (*get_features)(kernel_net_t *this); + + /** * Get our outgoing source address for a destination. * * Does a route lookup to get the source address used to reach dest. diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 6c4e711a4..ff1fdeef6 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1036,6 +1036,12 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this) return JOB_REQUEUE_DIRECT; } +METHOD(kernel_ipsec_t, get_features, kernel_feature_t, + private_kernel_netlink_ipsec_t *this) +{ + return KERNEL_ESP_V3_TFC; +} + /** * Get an SPI for a specific protocol from the kernel. */ @@ -2734,6 +2740,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() INIT(this, .public = { .interface = { + .get_features = _get_features, .get_spi = _get_spi, .get_cpi = _get_cpi, .add_sa = _add_sa, diff --git a/src/libimcv/ietf/ietf_attr_pa_tnc_error.c b/src/libimcv/ietf/ietf_attr_pa_tnc_error.c index f92022fe0..cc3ec28c5 100644 --- a/src/libimcv/ietf/ietf_attr_pa_tnc_error.c +++ b/src/libimcv/ietf/ietf_attr_pa_tnc_error.c @@ -325,22 +325,12 @@ METHOD(ietf_attr_pa_tnc_error_t, get_offset, u_int32_t, } /** - * Described in header. + * Generic constructor */ -pa_tnc_attr_t *ietf_attr_pa_tnc_error_create(pen_type_t error_code, - chunk_t msg_info) +static private_ietf_attr_pa_tnc_error_t* create_generic() { private_ietf_attr_pa_tnc_error_t *this; - if (error_code.vendor_id == PEN_IETF) - { - msg_info.len = PA_ERROR_MSG_INFO_SIZE; - } - else if (msg_info.len > PA_ERROR_MSG_INFO_MAX_SIZE) - { - msg_info.len = PA_ERROR_MSG_INFO_MAX_SIZE; - } - INIT(this, .public = { .pa_tnc_attribute = { @@ -360,11 +350,33 @@ pa_tnc_attr_t *ietf_attr_pa_tnc_error_create(pen_type_t error_code, .get_offset = _get_offset, }, .type = { PEN_IETF, IETF_ATTR_PA_TNC_ERROR }, - .error_code = error_code, - .msg_info = chunk_clone(msg_info), .ref = 1, ); + return this; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *ietf_attr_pa_tnc_error_create(pen_type_t error_code, + chunk_t msg_info) +{ + private_ietf_attr_pa_tnc_error_t *this; + + if (error_code.vendor_id == PEN_IETF) + { + msg_info.len = PA_ERROR_MSG_INFO_SIZE; + } + else if (msg_info.len > PA_ERROR_MSG_INFO_MAX_SIZE) + { + msg_info.len = PA_ERROR_MSG_INFO_MAX_SIZE; + } + + this = create_generic(); + this->error_code = error_code; + this->msg_info = chunk_clone(msg_info); + return &this->public.pa_tnc_attribute; } @@ -380,30 +392,10 @@ pa_tnc_attr_t *ietf_attr_pa_tnc_error_create_with_offset(pen_type_t error_code, /* the first 8 bytes of the erroneous PA-TNC message are sent back */ msg_info.len = PA_ERROR_MSG_INFO_SIZE; - INIT(this, - .public = { - .pa_tnc_attribute = { - .get_type = _get_type, - .get_value = _get_value, - .get_noskip_flag = _get_noskip_flag, - .set_noskip_flag = _set_noskip_flag, - .build = _build, - .process = _process, - .get_ref = _get_ref, - .destroy = _destroy, - }, - .get_error_code = _get_error_code, - .get_msg_info = _get_msg_info, - .get_attr_info = _get_attr_info, - .set_attr_info = _set_attr_info, - .get_offset = _get_offset, - }, - .type = { PEN_IETF, IETF_ATTR_PA_TNC_ERROR }, - .error_code = error_code, - .msg_info = chunk_clone(msg_info), - .error_offset = error_offset, - .ref = 1, - ); + this = create_generic(); + this->error_code = error_code; + this->msg_info = chunk_clone(msg_info); + this->error_offset = error_offset; return &this->public.pa_tnc_attribute; } @@ -415,30 +407,8 @@ pa_tnc_attr_t *ietf_attr_pa_tnc_error_create_from_data(chunk_t data) { private_ietf_attr_pa_tnc_error_t *this; - INIT(this, - .public = { - .pa_tnc_attribute = { - .get_type = _get_type, - .get_value = _get_value, - .get_noskip_flag = _get_noskip_flag, - .set_noskip_flag = _set_noskip_flag, - .build = _build, - .process = _process, - .get_ref = _get_ref, - .destroy = _destroy, - }, - .get_error_code = _get_error_code, - .get_msg_info = _get_msg_info, - .get_attr_info = _get_attr_info, - .set_attr_info = _set_attr_info, - .get_offset = _get_offset, - }, - .type = { PEN_IETF, IETF_ATTR_PA_TNC_ERROR }, - .value = chunk_clone(data), - .ref = 1, - ); + this = create_generic(); + this->value = chunk_clone(data); return &this->public.pa_tnc_attribute; } - - diff --git a/src/libimcv/ietf/ietf_attr_remediation_instr.c b/src/libimcv/ietf/ietf_attr_remediation_instr.c index f3b4e83dd..d28631ec7 100644 --- a/src/libimcv/ietf/ietf_attr_remediation_instr.c +++ b/src/libimcv/ietf/ietf_attr_remediation_instr.c @@ -194,14 +194,16 @@ METHOD(pa_tnc_attr_t, process, status_t, DBG1(DBG_TNC, "insufficient data for IETF remediation string"); goto end; } + *offset += 4; + pos = memchr(this->string.ptr, '\0', this->string.len); if (pos) { DBG1(DBG_TNC, "nul termination in IETF remediation string"); - *offset += 1 + (pos - this->string.ptr); + *offset += (pos - this->string.ptr); goto end; } - *offset += 4 + this->string.len; + *offset += this->string.len; if (!reader->read_data8(reader, &this->lang_code)) { @@ -246,12 +248,6 @@ METHOD(ietf_attr_remediation_instr_t, get_parameters, chunk_t, return this->parameters; } -METHOD(ietf_attr_remediation_instr_t, get_uri, chunk_t, - private_ietf_attr_remediation_instr_t *this) -{ - return this->parameters; -} - METHOD(ietf_attr_remediation_instr_t, get_string, chunk_t, private_ietf_attr_remediation_instr_t *this, chunk_t *lang_code) { @@ -284,7 +280,7 @@ pa_tnc_attr_t *ietf_attr_remediation_instr_create(pen_type_t parameters_type, }, .get_parameters_type = _get_parameters_type, .get_parameters = _get_parameters, - .get_uri = _get_uri, + .get_uri = _get_parameters, .get_string = _get_string, }, .type = { PEN_IETF, IETF_ATTR_REMEDIATION_INSTRUCTIONS }, @@ -350,7 +346,7 @@ pa_tnc_attr_t *ietf_attr_remediation_instr_create_from_data(chunk_t data) }, .get_parameters_type = _get_parameters_type, .get_parameters = _get_parameters, - .get_uri = _get_uri, + .get_uri = _get_parameters, .get_string = _get_string, }, .type = { PEN_IETF, IETF_ATTR_REMEDIATION_INSTRUCTIONS }, diff --git a/src/libimcv/imc/imc_agent.c b/src/libimcv/imc/imc_agent.c index 161623477..f309abe74 100644 --- a/src/libimcv/imc/imc_agent.c +++ b/src/libimcv/imc/imc_agent.c @@ -190,8 +190,6 @@ METHOD(imc_agent_t, bind_functions, TNC_Result, { this->reserve_additional_id = NULL; } - DBG2(DBG_IMC, "IMC %u \"%s\" provided with bind function", - this->id, this->name); if (this->report_message_types_long) { diff --git a/src/libimcv/imc/imc_msg.c b/src/libimcv/imc/imc_msg.c index 050e63f32..1fc3d3be5 100644 --- a/src/libimcv/imc/imc_msg.c +++ b/src/libimcv/imc/imc_msg.c @@ -91,6 +91,12 @@ METHOD(imc_msg_t, get_dst_id, TNC_UInt32, return this->dst_id; } +METHOD(imc_msg_t, get_msg_type, pen_type_t, + private_imc_msg_t *this) +{ + return this->msg_type; +} + METHOD(imc_msg_t, send_, TNC_Result, private_imc_msg_t *this, bool excl) { @@ -380,6 +386,7 @@ imc_msg_t *imc_msg_create(imc_agent_t *agent, imc_state_t *state, .public = { .get_src_id = _get_src_id, .get_dst_id = _get_dst_id, + .get_msg_type = _get_msg_type, .send = _send_, .receive = _receive, .add_attribute = _add_attribute, @@ -454,4 +461,3 @@ imc_msg_t *imc_msg_create_from_long_data(imc_agent_t *agent, imc_state_t *state, return &this->public; } - diff --git a/src/libimcv/imc/imc_msg.h b/src/libimcv/imc/imc_msg.h index 6dd712e84..9a4623ae9 100644 --- a/src/libimcv/imc/imc_msg.h +++ b/src/libimcv/imc/imc_msg.h @@ -48,6 +48,13 @@ struct imc_msg_t { TNC_UInt32 (*get_dst_id)(imc_msg_t *this); /** + * Get the PA-TNC message type. + * + * @return message type + */ + pen_type_t (*get_msg_type)(imc_msg_t *this); + + /** * Sends one or multiple PA-TNC messages * * @param excl set the excl message flag if supported diff --git a/src/libimcv/imv/imv_agent.c b/src/libimcv/imv/imv_agent.c index 6a33e396c..78e584dfb 100644 --- a/src/libimcv/imv/imv_agent.c +++ b/src/libimcv/imv/imv_agent.c @@ -18,8 +18,11 @@ #include "ietf/ietf_attr_assess_result.h" #include <tncif_names.h> +#include <tncif_identity.h> #include <utils/debug.h> +#include <collections/linked_list.h> +#include <bio/bio_reader.h> #include <threading/rwlock.h> typedef struct private_imv_agent_t private_imv_agent_t; @@ -210,8 +213,6 @@ METHOD(imv_agent_t, bind_functions, TNC_Result, { this->reserve_additional_id = NULL; } - DBG2(DBG_IMV, "IMV %u \"%s\" provided with bind function", - this->id, this->name); if (this->report_message_types_long) { @@ -352,12 +353,59 @@ static u_int32_t get_uint_attribute(private_imv_agent_t *this, TNC_ConnectionID return 0; } +/** + * Read a TNC identity attribute + */ +static linked_list_t* get_identity_attribute(private_imv_agent_t *this, + TNC_ConnectionID id, + TNC_AttributeID attribute_id) +{ + TNC_UInt32 len; + char buf[2048]; + u_int32_t count; + tncif_identity_t *tnc_id; + bio_reader_t *reader; + linked_list_t *list; + + list = linked_list_create(); + + if (!this->get_attribute || + this->get_attribute(this->id, id, attribute_id, sizeof(buf), buf, &len) + != TNC_RESULT_SUCCESS || len > sizeof(buf)) + { + return list; + } + + reader = bio_reader_create(chunk_create(buf, len)); + if (!reader->read_uint32(reader, &count)) + { + goto end; + } + while (count--) + { + tnc_id = tncif_identity_create_empty(); + if (!tnc_id->process(tnc_id, reader)) + { + tnc_id->destroy(tnc_id); + goto end; + } + list->insert_last(list, tnc_id); + } + +end: + reader->destroy(reader); + return list; + } + METHOD(imv_agent_t, create_state, TNC_Result, private_imv_agent_t *this, imv_state_t *state) { TNC_ConnectionID conn_id; char *tnccs_p = NULL, *tnccs_v = NULL, *t_p = NULL, *t_v = NULL; bool has_long = FALSE, has_excl = FALSE, has_soh = FALSE; + linked_list_t *ar_identities; + enumerator_t *enumerator; + tncif_identity_t *tnc_id; u_int32_t max_msg_len; conn_id = state->get_connection_id(state); @@ -378,6 +426,7 @@ METHOD(imv_agent_t, create_state, TNC_Result, t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL); t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION); max_msg_len = get_uint_attribute(this, conn_id, TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE); + ar_identities = get_identity_attribute(this, conn_id, TNC_ATTRIBUTEID_AR_IDENTITIES); state->set_flags(state, has_long, has_excl); state->set_max_msg_len(state, max_msg_len); @@ -389,6 +438,64 @@ METHOD(imv_agent_t, create_state, TNC_Result, DBG2(DBG_IMV, " over %s %s with maximum PA-TNC message size of %u bytes", t_p ? t_p:"?", t_v ? t_v :"?", max_msg_len); + enumerator = ar_identities->create_enumerator(ar_identities); + while (enumerator->enumerate(enumerator, &tnc_id)) + { + pen_type_t id_type, subject_type, auth_type; + int tcg_id_type, tcg_subject_type, tcg_auth_type; + chunk_t id_value; + id_type_t ike_type; + identification_t *id; + + id_type = tnc_id->get_identity_type(tnc_id); + id_value = tnc_id->get_identity_value(tnc_id); + subject_type = tnc_id->get_subject_type(tnc_id); + auth_type = tnc_id->get_auth_type(tnc_id); + + tcg_id_type = (id_type.vendor_id == PEN_TCG) ? + id_type.type : TNC_ID_UNKNOWN; + tcg_subject_type = (subject_type.vendor_id == PEN_TCG) ? + subject_type.type : TNC_SUBJECT_UNKNOWN; + tcg_auth_type = (auth_type.vendor_id == PEN_TCG) ? + auth_type.type : TNC_AUTH_UNKNOWN; + + switch (tcg_id_type) + { + case TNC_ID_IPV4_ADDR: + ike_type = ID_IPV4_ADDR; + break; + case TNC_ID_IPV6_ADDR: + ike_type = ID_IPV6_ADDR; + break; + case TNC_ID_FQDN: + case TNC_ID_USER_NAME: + ike_type = ID_FQDN; + break; + case TNC_ID_RFC822_ADDR: + ike_type = ID_RFC822_ADDR; + break; + case TNC_ID_DER_ASN1_DN: + ike_type = ID_DER_ASN1_DN; + break; + case TNC_ID_DER_ASN1_GN: + ike_type = ID_IPV4_ADDR; + break; + case TNC_ID_UNKNOWN: + default: + ike_type = ID_KEY_ID; + break; + } + + id = identification_create_from_encoding(ike_type, id_value); + DBG2(DBG_IMV, "%N identity '%Y' authenticated by %N", + TNC_Subject_names, tcg_subject_type, id, + TNC_Authentication_names, tcg_auth_type); + id->destroy(id); + } + enumerator->destroy(enumerator); + + ar_identities->destroy_offset(ar_identities, + offsetof(tncif_identity_t, destroy)); free(tnccs_p); free(tnccs_v); free(t_p); diff --git a/src/libimcv/imv/imv_msg.c b/src/libimcv/imv/imv_msg.c index 4ed19dd13..496d0ee1c 100644 --- a/src/libimcv/imv/imv_msg.c +++ b/src/libimcv/imv/imv_msg.c @@ -102,6 +102,12 @@ METHOD(imv_msg_t, set_msg_type, void, } } +METHOD(imv_msg_t, get_msg_type, pen_type_t, + private_imv_msg_t *this) +{ + return this->msg_type; +} + METHOD(imv_msg_t, add_attribute, void, private_imv_msg_t *this, pa_tnc_attr_t *attr) { @@ -352,6 +358,7 @@ imv_msg_t *imv_msg_create(imv_agent_t *agent, imv_state_t *state, .get_src_id = _get_src_id, .get_dst_id = _get_dst_id, .set_msg_type = _set_msg_type, + .get_msg_type = _get_msg_type, .send = _send_, .send_assessment = _send_assessment, .receive = _receive, diff --git a/src/libimcv/imv/imv_msg.h b/src/libimcv/imv/imv_msg.h index b639712e8..2a2810170 100644 --- a/src/libimcv/imv/imv_msg.h +++ b/src/libimcv/imv/imv_msg.h @@ -55,6 +55,13 @@ struct imv_msg_t { void (*set_msg_type)(imv_msg_t *this, pen_type_t msg_type); /** + * Get the type of a PA-TNC message. + * + * @return message type + */ + pen_type_t (*get_msg_type)(imv_msg_t *this); + + /** * Sends one or multiple PA-TNC messages * * @param excl set the excl message flag if supported diff --git a/src/libimcv/os_info/os_info.c b/src/libimcv/os_info/os_info.c index 13374c876..2c49cb01d 100644 --- a/src/libimcv/os_info/os_info.c +++ b/src/libimcv/os_info/os_info.c @@ -156,7 +156,7 @@ METHOD(os_info_t, get_uptime, time_t, { const char proc_uptime[] = "/proc/uptime"; FILE *file; - time_t uptime; + u_int uptime; file = fopen(proc_uptime, "r"); if (!file) diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.c b/src/libimcv/pa_tnc/pa_tnc_msg.c index 63445f3a1..d3aceee06 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.c +++ b/src/libimcv/pa_tnc/pa_tnc_msg.c @@ -284,6 +284,18 @@ METHOD(pa_tnc_msg_t, process, status_t, } DBG3(DBG_TNC, "%B", &value); + if (vendor_id == PEN_RESERVED) + { + error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + this->encoding, offset + 1); + goto err; + } + if (type == IETF_ATTR_RESERVED) + { + error = ietf_attr_pa_tnc_error_create_with_offset(error_code, + this->encoding, offset + 4); + goto err; + } attr = imcv_pa_tnc_attributes->create(imcv_pa_tnc_attributes, vendor_id, type, value); if (!attr) diff --git a/src/libimcv/plugins/imv_os/imv_os.c b/src/libimcv/plugins/imv_os/imv_os.c index 65538df07..ecc6cfc4f 100644 --- a/src/libimcv/plugins/imv_os/imv_os.c +++ b/src/libimcv/plugins/imv_os/imv_os.c @@ -360,7 +360,9 @@ static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) out_msg->add_attribute(out_msg, attr); } - if (fatal_error) + if (fatal_error || + (os_state->get_attribute_request(os_state) && + os_state->get_info(os_state, NULL, NULL, NULL) == NULL)) { state->set_recommendation(state, TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, @@ -371,7 +373,8 @@ static TNC_Result receive_message(imv_state_t *state, imv_msg_t *in_msg) /* If all Installed Packages attributes were received, go to assessment */ if (!assessment && !os_state->get_package_request(os_state) && - !os_state->get_angel_count(os_state)) + !os_state->get_angel_count(os_state) && + os_state->get_info(os_state, NULL, NULL, NULL)) { int device_id, count, count_update, count_blacklist, count_ok; u_int os_settings; @@ -518,6 +521,8 @@ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, { imv_state_t *state; imv_os_state_t *os_state; + TNC_IMV_Action_Recommendation rec; + TNC_IMV_Evaluation_Result eval; TNC_Result result = TNC_RESULT_SUCCESS; if (!imv_os) @@ -531,6 +536,18 @@ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, } os_state = (imv_os_state_t*)state; + state->get_recommendation(state, &rec, &eval); + + /* + * Don't send an attribute request if an evaluation is available + * or if an attribute request has already been sent + */ + if (eval != TNC_IMV_EVALUATION_RESULT_DONT_KNOW || + os_state->get_attribute_request(os_state)) + { + return TNC_RESULT_SUCCESS; + } + if (os_state->get_info(os_state, NULL, NULL, NULL) == NULL) { imv_msg_t *out_msg; @@ -548,6 +565,7 @@ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FORWARDING_ENABLED); attr_cast->add(attr_cast, PEN_IETF, IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED); out_msg->add_attribute(out_msg, attr); + os_state->set_attribute_request(os_state, TRUE); /* send PA-TNC message with excl flag not set */ result = out_msg->send(out_msg, FALSE); diff --git a/src/libimcv/plugins/imv_os/imv_os_state.c b/src/libimcv/plugins/imv_os/imv_os_state.c index ca6e050f7..00e0424fb 100644 --- a/src/libimcv/plugins/imv_os/imv_os_state.c +++ b/src/libimcv/plugins/imv_os/imv_os_state.c @@ -137,6 +137,11 @@ struct private_imv_os_state_t { int count_ok; /** + * Attribute request sent - mandatory response expected + */ + bool attribute_request; + + /** * OS Installed Package request sent - mandatory response expected */ bool package_request; @@ -506,6 +511,18 @@ METHOD(imv_os_state_t, get_count, void, } } +METHOD(imv_os_state_t, set_attribute_request, void, + private_imv_os_state_t *this, bool set) +{ + this->attribute_request = set; +} + +METHOD(imv_os_state_t, get_attribute_request, bool, + private_imv_os_state_t *this) +{ + return this->attribute_request; +} + METHOD(imv_os_state_t, set_package_request, void, private_imv_os_state_t *this, bool set) { @@ -597,6 +614,8 @@ imv_state_t *imv_os_state_create(TNC_ConnectionID connection_id) .get_info = _get_info, .set_count = _set_count, .get_count = _get_count, + .set_attribute_request = _set_attribute_request, + .get_attribute_request = _get_attribute_request, .set_package_request = _set_package_request, .get_package_request = _get_package_request, .set_device_id = _set_device_id, diff --git a/src/libimcv/plugins/imv_os/imv_os_state.h b/src/libimcv/plugins/imv_os/imv_os_state.h index 05abdbb6c..d3e319171 100644 --- a/src/libimcv/plugins/imv_os/imv_os_state.h +++ b/src/libimcv/plugins/imv_os/imv_os_state.h @@ -87,6 +87,21 @@ struct imv_os_state_t { */ void (*get_count)(imv_os_state_t *this, int *count, int *count_update, int *count_blacklist, int *count_ok); + + /** + * Set/reset attribute request status + * + * @param set TRUE to set, FALSE to clear + */ + void (*set_attribute_request)(imv_os_state_t *this, bool set); + + /** + * Get attribute request status + * + * @return TRUE if set, FALSE if unset + */ + bool (*get_attribute_request)(imv_os_state_t *this); + /** * Set/reset OS Installed Packages request status * diff --git a/src/libipsec/esp_packet.c b/src/libipsec/esp_packet.c index 16cc687ef..43a3c2a97 100644 --- a/src/libipsec/esp_packet.c +++ b/src/libipsec/esp_packet.c @@ -97,6 +97,18 @@ METHOD(packet_t, set_data, void, return this->packet->set_data(this->packet, data); } +METHOD(packet_t, get_dscp, u_int8_t, + private_esp_packet_t *this) +{ + return this->packet->get_dscp(this->packet); +} + +METHOD(packet_t, set_dscp, void, + private_esp_packet_t *this, u_int8_t value) +{ + this->packet->set_dscp(this->packet, value); +} + METHOD(packet_t, skip_bytes, void, private_esp_packet_t *this, size_t bytes) { @@ -411,6 +423,8 @@ static private_esp_packet_t *esp_packet_create_internal(packet_t *packet) .get_destination = _get_destination, .get_data = _get_data, .set_data = _set_data, + .get_dscp = _get_dscp, + .set_dscp = _set_dscp, .skip_bytes = _skip_bytes, .clone = _clone, .destroy = _destroy, diff --git a/src/libpttls/Makefile.am b/src/libpttls/Makefile.am new file mode 100644 index 000000000..d19af0365 --- /dev/null +++ b/src/libpttls/Makefile.am @@ -0,0 +1,10 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libtls \ + -I$(top_srcdir)/src/libtncif -I$(top_srcdir)/src/libtnccs + +ipseclib_LTLIBRARIES = libpttls.la +libpttls_la_LIBADD = $(top_builddir)/src/libtls/libtls.la +libpttls_la_SOURCES = pt_tls.c pt_tls.h \ + pt_tls_client.c pt_tls_client.h \ + pt_tls_server.c pt_tls_server.h \ + pt_tls_dispatcher.c pt_tls_dispatcher.h diff --git a/src/libpttls/pt_tls.c b/src/libpttls/pt_tls.c new file mode 100644 index 000000000..0fee343b8 --- /dev/null +++ b/src/libpttls/pt_tls.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pt_tls.h" + +#include <utils/debug.h> + +/* + * PT-TNC Message format: + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | Message Type Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Message Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Message Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Message Identifier | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Message Value (e.g. PB-TNC Batch) . . . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/** + * Read a chunk of data from TLS, returning a reader for it + */ +static bio_reader_t* read_tls(tls_socket_t *tls, size_t len) +{ + ssize_t got, total = 0; + char *buf; + + buf = malloc(len); + while (total < len) + { + got = tls->read(tls, buf + total, len - total, TRUE); + if (got <= 0) + { + free(buf); + return NULL; + } + total += got; + } + return bio_reader_create_own(chunk_create(buf, len)); +} + +/** + * Read a PT-TLS message, return header data + */ +bio_reader_t* pt_tls_read(tls_socket_t *tls, u_int32_t *vendor, + u_int32_t *type, u_int32_t *identifier) +{ + bio_reader_t *reader; + u_int32_t len; + u_int8_t reserved; + + reader = read_tls(tls, PT_TLS_HEADER_LEN); + if (!reader) + { + return NULL; + } + if (!reader->read_uint8(reader, &reserved) || + !reader->read_uint24(reader, vendor) || + !reader->read_uint32(reader, type) || + !reader->read_uint32(reader, &len) || + !reader->read_uint32(reader, identifier)) + { + reader->destroy(reader); + return NULL; + } + reader->destroy(reader); + + if (len < PT_TLS_HEADER_LEN) + { + DBG1(DBG_TNC, "received short PT-TLS header (%d bytes)", len); + return NULL; + } + return read_tls(tls, len - PT_TLS_HEADER_LEN); +} + +/** + * Prepend a PT-TLS header to a writer, send data, destroy writer + */ +bool pt_tls_write(tls_socket_t *tls, bio_writer_t *writer, + pt_tls_message_type_t type, u_int32_t identifier) +{ + bio_writer_t *header; + ssize_t len; + chunk_t data; + + data = writer->get_buf(writer); + len = PT_TLS_HEADER_LEN + data.len; + header = bio_writer_create(len); + header->write_uint8(header, 0); + header->write_uint24(header, 0); + header->write_uint32(header, type); + header->write_uint32(header, len); + header->write_uint32(header, identifier); + + header->write_data(header, data); + writer->destroy(writer); + + data = header->get_buf(header); + len = tls->write(tls, data.ptr, data.len); + header->destroy(header); + + return len == data.len; +} diff --git a/src/libpttls/pt_tls.h b/src/libpttls/pt_tls.h new file mode 100644 index 000000000..8b2422540 --- /dev/null +++ b/src/libpttls/pt_tls.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pt_tls pt_tls + * @{ @ingroup pt_tls + */ + +#ifndef PT_TLS_H_ +#define PT_TLS_H_ + +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> +#include <tls_socket.h> + +/** + * PT-TLS version we support + */ +#define PT_TLS_VERSION 1 + +/** + * Length of a PT-TLS header + */ +#define PT_TLS_HEADER_LEN 16 + +typedef enum pt_tls_message_type_t pt_tls_message_type_t; + +/** + * Message types, as defined by NEA PT-TLS + */ +enum pt_tls_message_type_t { + PT_TLS_EXPERIMENTAL = 0, + PT_TLS_VERSION_REQUEST = 1, + PT_TLS_VERSION_RESPONSE = 2, + PT_TLS_SASL_MECHS = 3, + PT_TLS_SASL_MECH_SELECTION = 4, + PT_TLS_SASL_AUTH_DATA = 5, + PT_TLS_SASL_RESULT = 6, + PT_TLS_PB_TNC_BATCH = 7, + PT_TLS_ERROR = 8, +}; + +/** + * Read a PT-TLS message, create reader over Message Value. + * + * @param tls TLS socket to read from + * @param vendor receives Message Type Vendor ID from header + * @param type receives Message Type from header + * @param identifier receives Message Identifer + * @return reader over message value, NULL on error + */ +bio_reader_t* pt_tls_read(tls_socket_t *tls, u_int32_t *vendor, + u_int32_t *type, u_int32_t *identifier); + +/** + * Prepend a PT-TLS header to a writer, send data, destroy writer. + * + * @param tls TLS socket to write to + * @param writer prepared Message value to write + * @param type Message Type to write + * @param identifier Message Identifier to write + * @return TRUE if data written successfully + */ +bool pt_tls_write(tls_socket_t *tls, bio_writer_t *writer, + pt_tls_message_type_t type, u_int32_t identifier); + +#endif /** PT_TLS_H_ @}*/ diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c new file mode 100644 index 000000000..948d92982 --- /dev/null +++ b/src/libpttls/pt_tls_client.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pt_tls_client.h" +#include "pt_tls.h" + +#include <tls_socket.h> +#include <utils/debug.h> + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +typedef struct private_pt_tls_client_t private_pt_tls_client_t; + +/** + * Private data of an pt_tls_client_t object. + */ +struct private_pt_tls_client_t { + + /** + * Public pt_tls_client_t interface. + */ + pt_tls_client_t public; + + /** + * TLS secured socket used by PT-TLS + */ + tls_socket_t *tls; + + /** + * Server address/port + */ + host_t *address; + + /** + * Server identity + */ + identification_t *id; + + /** + * Current PT-TLS message identifier + */ + u_int32_t identifier; +}; + +/** + * Establish TLS secured TCP connection to TNC server + */ +static bool make_connection(private_pt_tls_client_t *this) +{ + int fd; + + fd = socket(this->address->get_family(this->address), SOCK_STREAM, 0); + if (fd == -1) + { + DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno)); + return FALSE; + } + if (connect(fd, this->address->get_sockaddr(this->address), + *this->address->get_sockaddr_len(this->address)) == -1) + { + DBG1(DBG_TNC, "connecting to PT-TLS server failed: %s", strerror(errno)); + close(fd); + return FALSE; + } + + this->tls = tls_socket_create(FALSE, this->id, NULL, fd, NULL); + if (!this->tls) + { + close(fd); + return FALSE; + } + return TRUE; +} + +/** + * Negotiate PT-TLS version + */ +static bool negotiate_version(private_pt_tls_client_t *this) +{ + bio_writer_t *writer; + bio_reader_t *reader; + u_int32_t type, vendor, identifier, reserved; + u_int8_t version; + + DBG1(DBG_TNC, "sending offer for PT-TLS version %d", PT_TLS_VERSION); + + writer = bio_writer_create(4); + writer->write_uint8(writer, 0); + writer->write_uint8(writer, PT_TLS_VERSION); + writer->write_uint8(writer, PT_TLS_VERSION); + writer->write_uint8(writer, PT_TLS_VERSION); + if (!pt_tls_write(this->tls, writer, PT_TLS_VERSION_REQUEST, + this->identifier++)) + { + return FALSE; + } + + reader = pt_tls_read(this->tls, &vendor, &type, &identifier); + if (!reader) + { + return FALSE; + } + if (vendor != 0 || type != PT_TLS_VERSION_RESPONSE || + !reader->read_uint24(reader, &reserved) || + !reader->read_uint8(reader, &version) || + version != PT_TLS_VERSION) + { + DBG1(DBG_TNC, "PT-TLS version negotiation failed"); + reader->destroy(reader); + return FALSE; + } + reader->destroy(reader); + return TRUE; +} + +/** + * Authenticate session using SASL + */ +static bool authenticate(private_pt_tls_client_t *this) +{ + bio_reader_t *reader; + u_int32_t type, vendor, identifier; + + reader = pt_tls_read(this->tls, &vendor, &type, &identifier); + if (!reader) + { + return FALSE; + } + if (vendor != 0 || type != PT_TLS_SASL_MECHS) + { + DBG1(DBG_TNC, "PT-TLS authentication failed"); + reader->destroy(reader); + return FALSE; + } + + if (reader->remaining(reader)) + { /* mechanism list not empty, FAIL until we support it */ + reader->destroy(reader); + return FALSE; + } + DBG1(DBG_TNC, "PT-TLS authentication complete"); + reader->destroy(reader); + return TRUE; +} + +/** + * Perform assessment + */ +static bool assess(private_pt_tls_client_t *this, tls_t *tnccs) +{ + while (TRUE) + { + bio_writer_t *writer; + bio_reader_t *reader; + u_int32_t vendor, type, identifier; + chunk_t data; + + writer = bio_writer_create(32); + while (TRUE) + { + char buf[2048]; + size_t buflen, msglen; + + buflen = sizeof(buf); + switch (tnccs->build(tnccs, buf, &buflen, &msglen)) + { + case SUCCESS: + writer->destroy(writer); + return tnccs->is_complete(tnccs); + case FAILED: + default: + writer->destroy(writer); + return FALSE; + case INVALID_STATE: + writer->destroy(writer); + break; + case NEED_MORE: + writer->write_data(writer, chunk_create(buf, buflen)); + continue; + case ALREADY_DONE: + writer->write_data(writer, chunk_create(buf, buflen)); + if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH, + this->identifier++)) + { + return FALSE; + } + writer = bio_writer_create(32); + continue; + } + break; + } + + reader = pt_tls_read(this->tls, &vendor, &type, &identifier); + if (!reader) + { + return FALSE; + } + if (vendor == 0) + { + if (type == PT_TLS_ERROR) + { + DBG1(DBG_TNC, "received PT-TLS error"); + reader->destroy(reader); + return FALSE; + } + if (type != PT_TLS_PB_TNC_BATCH) + { + DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type); + reader->destroy(reader); + return FALSE; + } + data = reader->peek(reader); + switch (tnccs->process(tnccs, data.ptr, data.len)) + { + case SUCCESS: + reader->destroy(reader); + return tnccs->is_complete(tnccs); + case FAILED: + default: + reader->destroy(reader); + return FALSE; + case NEED_MORE: + break; + } + } + else + { + DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message"); + } + reader->destroy(reader); + } +} + +METHOD(pt_tls_client_t, run_assessment, status_t, + private_pt_tls_client_t *this, tnccs_t *tnccs) +{ + if (!this->tls) + { + if (!make_connection(this)) + { + return FAILED; + } + } + if (!negotiate_version(this)) + { + return FAILED; + } + if (!authenticate(this)) + { + return FAILED; + } + if (!assess(this, (tls_t*)tnccs)) + { + return FAILED; + } + return SUCCESS; +} + + +METHOD(pt_tls_client_t, destroy, void, + private_pt_tls_client_t *this) +{ + if (this->tls) + { + close(this->tls->get_fd(this->tls)); + this->tls->destroy(this->tls); + } + this->address->destroy(this->address); + this->id->destroy(this->id); + free(this); +} + +/** + * See header + */ +pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id) +{ + private_pt_tls_client_t *this; + + INIT(this, + .public = { + .run_assessment = _run_assessment, + .destroy = _destroy, + }, + .address = address, + .id = id, + ); + + return &this->public; +} diff --git a/src/libpttls/pt_tls_client.h b/src/libpttls/pt_tls_client.h new file mode 100644 index 000000000..2e0553641 --- /dev/null +++ b/src/libpttls/pt_tls_client.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pt_tls_client pt_tls_client + * @{ @ingroup pt_tls + */ + +#ifndef PT_TLS_CLIENT_H_ +#define PT_TLS_CLIENT_H_ + +#include <networking/host.h> +#include <utils/identification.h> + +#include <tnc/tnccs/tnccs.h> + +typedef struct pt_tls_client_t pt_tls_client_t; + +/** + * IF-T for TLS aka PT-TLS transport client. + */ +struct pt_tls_client_t { + + /** + * Perform an assessment. + * + * @param tnccs upper layer TNC client used for assessment + * @return status of assessment + */ + status_t (*run_assessment)(pt_tls_client_t *this, tnccs_t *tnccs); + + /** + * Destroy a pt_tls_client_t. + */ + void (*destroy)(pt_tls_client_t *this); +}; + +/** + * Create a pt_tls_client instance. + * + * @param address address/port to run assessments against, gets owned + * @param id server identity to use for authentication, gets owned + * @return PT-TLS context + */ +pt_tls_client_t *pt_tls_client_create(host_t *address, identification_t *id); + +#endif /** PT_TLS_CLIENT_H_ @}*/ diff --git a/src/libpttls/pt_tls_dispatcher.c b/src/libpttls/pt_tls_dispatcher.c new file mode 100644 index 000000000..813580cd0 --- /dev/null +++ b/src/libpttls/pt_tls_dispatcher.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pt_tls_dispatcher.h" +#include "pt_tls_server.h" + +#include <threading/thread.h> +#include <utils/debug.h> +#include <processing/jobs/callback_job.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +typedef struct private_pt_tls_dispatcher_t private_pt_tls_dispatcher_t; + +/** + * Private data of an pt_tls_dispatcher_t object. + */ +struct private_pt_tls_dispatcher_t { + + /** + * Public pt_tls_dispatcher_t interface. + */ + pt_tls_dispatcher_t public; + + /** + * Listening socket + */ + int fd; + + /** + * Server identity + */ + identification_t *server; + + /** + * Peer identity + */ + identification_t *peer; + + /** + * TNCCS protocol handler constructor + */ + pt_tls_tnccs_constructor_t *create; +}; + +/** + * Open listening server socket + */ +static bool open_socket(private_pt_tls_dispatcher_t *this, host_t *host) +{ + this->fd = socket(AF_INET, SOCK_STREAM, 0); + if (this->fd == -1) + { + DBG1(DBG_TNC, "opening PT-TLS socket failed: %s", strerror(errno)); + return FALSE; + } + if (bind(this->fd, host->get_sockaddr(host), + *host->get_sockaddr_len(host)) == -1) + { + DBG1(DBG_TNC, "binding to PT-TLS socket failed: %s", strerror(errno)); + return FALSE; + } + if (listen(this->fd, 5) == -1) + { + DBG1(DBG_TNC, "listen on PT-TLS socket failed: %s", strerror(errno)); + return FALSE; + } + return TRUE; +} + +/** + * Handle a single PT-TLS client connection + */ +static job_requeue_t handle(pt_tls_server_t *connection) +{ + while (TRUE) + { + switch (connection->handle(connection)) + { + case NEED_MORE: + continue; + case FAILED: + case SUCCESS: + default: + break; + } + break; + } + return JOB_REQUEUE_NONE; +} + +/** + * Clean up connection state + */ +static void cleanup(pt_tls_server_t *connection) +{ + int fd; + + fd = connection->get_fd(connection); + connection->destroy(connection); + close(fd); +} + +METHOD(pt_tls_dispatcher_t, dispatch, void, + private_pt_tls_dispatcher_t *this, + pt_tls_tnccs_constructor_t *create) +{ + while (TRUE) + { + pt_tls_server_t *connection; + tnccs_t *tnccs; + bool old; + int fd; + + old = thread_cancelability(TRUE); + fd = accept(this->fd, NULL, NULL); + thread_cancelability(old); + if (fd == -1) + { + DBG1(DBG_TNC, "accepting PT-TLS failed: %s", strerror(errno)); + continue; + } + + tnccs = create(this->server, this->peer); + if (!tnccs) + { + close(fd); + continue; + } + connection = pt_tls_server_create(this->server, fd, tnccs); + if (!connection) + { + close(fd); + continue; + } + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((callback_job_cb_t)handle, + connection, (void*)cleanup, + (callback_job_cancel_t)return_false, + JOB_PRIO_CRITICAL)); + } +} + +METHOD(pt_tls_dispatcher_t, destroy, void, + private_pt_tls_dispatcher_t *this) +{ + if (this->fd != -1) + { + close(this->fd); + } + this->server->destroy(this->server); + this->peer->destroy(this->peer); + free(this); +} + +/** + * See header + */ +pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address, + identification_t *id) +{ + private_pt_tls_dispatcher_t *this; + + INIT(this, + .public = { + .dispatch = _dispatch, + .destroy = _destroy, + }, + .server = id, + /* we currently don't authenticate the peer, use %any identity */ + .peer = identification_create_from_encoding(ID_ANY, chunk_empty), + .fd = -1, + ); + + if (!open_socket(this, address)) + { + address->destroy(address); + destroy(this); + return NULL; + } + address->destroy(address); + + return &this->public; +} diff --git a/src/libpttls/pt_tls_dispatcher.h b/src/libpttls/pt_tls_dispatcher.h new file mode 100644 index 000000000..3c6560baa --- /dev/null +++ b/src/libpttls/pt_tls_dispatcher.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pt_tls_dispatcher pt_tls_dispatcher + * @{ @ingroup pt_tls + */ + +#ifndef PT_TLS_DISPATCHER_H_ +#define PT_TLS_DISPATCHER_H_ + +#include <networking/host.h> +#include <utils/identification.h> + +#include <tnc/tnccs/tnccs.h> + +typedef struct pt_tls_dispatcher_t pt_tls_dispatcher_t; + +/** + * Constructor callback to create TNCCS to use within PT-TLS. + * + * @param server server identity + * @param peer peer identity + */ +typedef tnccs_t* (pt_tls_tnccs_constructor_t)(identification_t *server, + identification_t *peer); + +/** + * PT-TLS dispatcher service, handles PT-TLS connections as a server. + */ +struct pt_tls_dispatcher_t { + + /** + * Dispatch and handle PT-TLS connections. + * + * This call is blocking and a thread cancellation point. The passed + * constructor gets called for each dispatched connection. + * + * @param create TNCCS constructor function to use + */ + void (*dispatch)(pt_tls_dispatcher_t *this, + pt_tls_tnccs_constructor_t *create); + + /** + * Destroy a pt_tls_dispatcher_t. + */ + void (*destroy)(pt_tls_dispatcher_t *this); +}; + +/** + * Create a pt_tls_dispatcher instance. + * + * @param address server address with port to listen on, gets owned + * @param id TLS server identity, gets owned + * @return dispatcher service + */ +pt_tls_dispatcher_t *pt_tls_dispatcher_create(host_t *address, + identification_t *id); + +#endif /** PT_TLS_DISPATCHER_H_ @}*/ diff --git a/src/libpttls/pt_tls_server.c b/src/libpttls/pt_tls_server.c new file mode 100644 index 000000000..b525acb6f --- /dev/null +++ b/src/libpttls/pt_tls_server.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pt_tls_server.h" +#include "pt_tls.h" + +#include <utils/debug.h> + +typedef struct private_pt_tls_server_t private_pt_tls_server_t; + +/** + * Private data of an pt_tls_server_t object. + */ +struct private_pt_tls_server_t { + + /** + * Public pt_tls_server_t interface. + */ + pt_tls_server_t public; + + /** + * TLS protected socket + */ + tls_socket_t *tls; + + enum { + /* expecting version negotiation */ + PT_TLS_SERVER_VERSION, + /* expecting an SASL exchange */ + PT_TLS_SERVER_AUTH, + /* expecting TNCCS exchange */ + PT_TLS_SERVER_TNCCS, + /* terminating state */ + PT_TLS_SERVER_END, + } state; + + /** + * Message Identifier + */ + u_int32_t identifier; + + /** + * TNCCS protocol handler, implemented as tls_t + */ + tls_t *tnccs; +}; + +/** + * Negotiate PT-TLS version + */ +static bool negotiate_version(private_pt_tls_server_t *this) +{ + bio_reader_t *reader; + bio_writer_t *writer; + u_int32_t vendor, type, identifier; + u_int8_t reserved, vmin, vmax, vpref; + + reader = pt_tls_read(this->tls, &vendor, &type, &identifier); + if (!reader) + { + return FALSE; + } + if (vendor != 0 || type != PT_TLS_VERSION_REQUEST || + !reader->read_uint8(reader, &reserved) || + !reader->read_uint8(reader, &vmin) || + !reader->read_uint8(reader, &vmax) || + !reader->read_uint8(reader, &vpref)) + { + DBG1(DBG_TNC, "PT-TLS version negotiation failed"); + reader->destroy(reader); + return FALSE; + } + reader->destroy(reader); + + if (vmin > PT_TLS_VERSION || vmax < PT_TLS_VERSION) + { + /* TODO: send error */ + return FALSE; + } + + writer = bio_writer_create(4); + writer->write_uint24(writer, 0); + writer->write_uint8(writer, PT_TLS_VERSION); + + return pt_tls_write(this->tls, writer, PT_TLS_VERSION_RESPONSE, + this->identifier++); +} + +/** + * Authenticated PT-TLS session with SASL + */ +static bool authenticate(private_pt_tls_server_t *this) +{ + bio_writer_t *writer; + + /* send empty SASL mechanims list to skip authentication */ + writer = bio_writer_create(0); + return pt_tls_write(this->tls, writer, PT_TLS_SASL_MECHS, + this->identifier++); +} + +/** + * Perform assessment + */ +static bool assess(private_pt_tls_server_t *this, tls_t *tnccs) +{ + while (TRUE) + { + bio_writer_t *writer; + bio_reader_t *reader; + u_int32_t vendor, type, identifier; + chunk_t data; + + writer = bio_writer_create(32); + while (TRUE) + { + char buf[2048]; + size_t buflen, msglen; + + buflen = sizeof(buf); + switch (tnccs->build(tnccs, buf, &buflen, &msglen)) + { + case SUCCESS: + writer->destroy(writer); + return tnccs->is_complete(tnccs); + case FAILED: + default: + writer->destroy(writer); + return FALSE; + case INVALID_STATE: + writer->destroy(writer); + break; + case NEED_MORE: + writer->write_data(writer, chunk_create(buf, buflen)); + continue; + case ALREADY_DONE: + writer->write_data(writer, chunk_create(buf, buflen)); + if (!pt_tls_write(this->tls, writer, PT_TLS_PB_TNC_BATCH, + this->identifier++)) + { + return FALSE; + } + writer = bio_writer_create(32); + continue; + } + break; + } + + reader = pt_tls_read(this->tls, &vendor, &type, &identifier); + if (!reader) + { + return FALSE; + } + if (vendor == 0) + { + if (type == PT_TLS_ERROR) + { + DBG1(DBG_TNC, "received PT-TLS error"); + reader->destroy(reader); + return FALSE; + } + if (type != PT_TLS_PB_TNC_BATCH) + { + DBG1(DBG_TNC, "unexpected PT-TLS message: %d", type); + reader->destroy(reader); + return FALSE; + } + data = reader->peek(reader); + switch (tnccs->process(tnccs, data.ptr, data.len)) + { + case SUCCESS: + reader->destroy(reader); + return tnccs->is_complete(tnccs); + case FAILED: + default: + reader->destroy(reader); + return FALSE; + case NEED_MORE: + break; + } + } + else + { + DBG1(DBG_TNC, "ignoring vendor specific PT-TLS message"); + } + reader->destroy(reader); + } +} + +METHOD(pt_tls_server_t, handle, status_t, + private_pt_tls_server_t *this) +{ + switch (this->state) + { + case PT_TLS_SERVER_VERSION: + if (!negotiate_version(this)) + { + return FAILED; + } + DBG1(DBG_TNC, "negotiated PT-TLS version %d", PT_TLS_VERSION); + this->state = PT_TLS_SERVER_AUTH; + break; + case PT_TLS_SERVER_AUTH: + DBG1(DBG_TNC, "sending empty mechanism list to skip SASL"); + if (!authenticate(this)) + { + return FAILED; + } + this->state = PT_TLS_SERVER_TNCCS; + break; + case PT_TLS_SERVER_TNCCS: + if (!assess(this, (tls_t*)this->tnccs)) + { + return FAILED; + } + this->state = PT_TLS_SERVER_END; + return SUCCESS; + default: + return FAILED; + } + return NEED_MORE; +} + +METHOD(pt_tls_server_t, get_fd, int, + private_pt_tls_server_t *this) +{ + return this->tls->get_fd(this->tls); +} + +METHOD(pt_tls_server_t, destroy, void, + private_pt_tls_server_t *this) +{ + this->tnccs->destroy(this->tnccs); + this->tls->destroy(this->tls); + free(this); +} + +/** + * See header + */ +pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd, + tnccs_t *tnccs) +{ + private_pt_tls_server_t *this; + + INIT(this, + .public = { + .handle = _handle, + .get_fd = _get_fd, + .destroy = _destroy, + }, + .state = PT_TLS_SERVER_VERSION, + .tls = tls_socket_create(TRUE, server, NULL, fd, NULL), + .tnccs = (tls_t*)tnccs, + ); + + if (!this->tls) + { + this->tnccs->destroy(this->tnccs); + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libpttls/pt_tls_server.h b/src/libpttls/pt_tls_server.h new file mode 100644 index 000000000..244111b43 --- /dev/null +++ b/src/libpttls/pt_tls_server.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pt_tls_server pt_tls_server + * @{ @ingroup pt_tls + */ + +#ifndef PT_TLS_SERVER_H_ +#define PT_TLS_SERVER_H_ + +#include <utils/identification.h> + +#include <tnc/tnccs/tnccs.h> + +typedef struct pt_tls_server_t pt_tls_server_t; + +/** + * IF-T for TLS aka PT-TLS transport server. + */ +struct pt_tls_server_t { + + /** + * Handle assessment data read from socket. + * + * @return + * - NEED_MORE if more exchanges required, + * - SUCCESS if assessment complete + * - FAILED if assessment failed + */ + status_t (*handle)(pt_tls_server_t *this); + + /** + * Get the underlying client connection socket. + * + * @return socket fd, suitable to select() + */ + int (*get_fd)(pt_tls_server_t *this); + + /** + * Destroy a pt_tls_server_t. + */ + void (*destroy)(pt_tls_server_t *this); +}; + +/** + * Create a pt_tls_server connection instance. + * + * @param server TLS server identity + * @param fd client connection socket + * @param tnccs inner TNCCS protocol handler to use for this connection + * @return PT-TLS server + */ +pt_tls_server_t *pt_tls_server_create(identification_t *server, int fd, + tnccs_t *tnccs); + +#endif /** PT_TLS_SERVER_H_ @}*/ diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 65cfe5292..a46b0d9a1 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -28,6 +28,7 @@ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ +resolver/resolver_manager.c resolver/rr_set.c \ selectors/traffic_selector.c threading/thread.c threading/thread_value.c \ threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 9c4665eeb..8d6c4583a 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -26,6 +26,7 @@ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ +resolver/resolver_manager.c resolver/rr_set.c \ selectors/traffic_selector.c threading/thread.c threading/thread_value.c \ threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ @@ -63,6 +64,8 @@ database/database.h database/database_factory.h fetcher/fetcher.h \ fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \ networking/host.h networking/host_resolver.h networking/packet.h \ networking/tun_device.h \ +resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \ +resolver/rr.h resolver/resolver_manager.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \ processing/scheduler.h selectors/traffic_selector.h \ @@ -323,6 +326,13 @@ if MONOLITHIC endif endif +if USE_UNBOUND + SUBDIRS += plugins/unbound +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/unbound/libstrongswan-unbound.la +endif +endif + if USE_SOUP SUBDIRS += plugins/soup if MONOLITHIC diff --git a/src/libstrongswan/bio/bio_reader.c b/src/libstrongswan/bio/bio_reader.c index 17815d6c0..29b9e7279 100644 --- a/src/libstrongswan/bio/bio_reader.c +++ b/src/libstrongswan/bio/bio_reader.c @@ -36,6 +36,11 @@ struct private_bio_reader_t { * Remaining data to process */ chunk_t buf; + + /** + * Optional data to free during destruction + */ + chunk_t cleanup; }; METHOD(bio_reader_t, remaining, u_int32_t, @@ -302,6 +307,7 @@ METHOD(bio_reader_t, read_data32, bool, METHOD(bio_reader_t, destroy, void, private_bio_reader_t *this) { + free(this->cleanup.ptr); free(this); } @@ -339,3 +345,17 @@ bio_reader_t *bio_reader_create(chunk_t data) return &this->public; } + +/** + * See header + */ +bio_reader_t *bio_reader_create_own(chunk_t data) +{ + private_bio_reader_t *this; + + this = (private_bio_reader_t*)bio_reader_create(data); + + this->cleanup = data; + + return &this->public; +} diff --git a/src/libstrongswan/bio/bio_reader.h b/src/libstrongswan/bio/bio_reader.h index 3162f3eda..475422428 100644 --- a/src/libstrongswan/bio/bio_reader.h +++ b/src/libstrongswan/bio/bio_reader.h @@ -187,7 +187,18 @@ struct bio_reader_t { /** * Create a bio_reader instance. + * + * @param data data buffer, must survive lifetime of reader + * @return reader */ bio_reader_t *bio_reader_create(chunk_t data); -#endif /** bio_reader_H_ @}*/ +/** + * Create a bio_reader instance owning buffer. + * + * @param data data buffer, gets freed with destroy() + * @return reader + */ +bio_reader_t *bio_reader_create_own(chunk_t data); + +#endif /** BIO_READER_H_ @}*/ diff --git a/src/libstrongswan/credentials/auth_cfg.c b/src/libstrongswan/credentials/auth_cfg.c index a718de3dc..a2ffe0295 100644 --- a/src/libstrongswan/credentials/auth_cfg.c +++ b/src/libstrongswan/credentials/auth_cfg.c @@ -999,14 +999,15 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, { enumerator_t *enumerator; auth_cfg_t *clone; - entry_t *entry; + auth_rule_t type; + void *value; clone = auth_cfg_create(); /* this enumerator skips duplicates for rules we expect only once */ - enumerator = this->entries->create_enumerator(this->entries); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_enumerator(this); + while (enumerator->enumerate(enumerator, &type, &value)) { - switch (entry->type) + switch (type) { case AUTH_RULE_IDENTITY: case AUTH_RULE_EAP_IDENTITY: @@ -1014,8 +1015,8 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_RULE_GROUP: case AUTH_RULE_XAUTH_IDENTITY: { - identification_t *id = (identification_t*)entry->value; - clone->add(clone, entry->type, id->clone(id)); + identification_t *id = (identification_t*)value; + clone->add(clone, type, id->clone(id)); break; } case AUTH_RULE_CA_CERT: @@ -1025,8 +1026,8 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_HELPER_SUBJECT_CERT: case AUTH_HELPER_REVOCATION_CERT: { - certificate_t *cert = (certificate_t*)entry->value; - clone->add(clone, entry->type, cert->get_ref(cert)); + certificate_t *cert = (certificate_t*)value; + clone->add(clone, type, cert->get_ref(cert)); break; } case AUTH_RULE_XAUTH_BACKEND: @@ -1034,7 +1035,7 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_HELPER_IM_HASH_URL: case AUTH_HELPER_SUBJECT_HASH_URL: { - clone->add(clone, entry->type, strdup(entry->value)); + clone->add(clone, type, strdup(value)); break; } case AUTH_RULE_IDENTITY_LOOSE: @@ -1046,7 +1047,7 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_RULE_RSA_STRENGTH: case AUTH_RULE_ECDSA_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: - clone->add(clone, entry->type, (uintptr_t)entry->value); + clone->add(clone, type, (uintptr_t)value); break; case AUTH_RULE_MAX: break; diff --git a/src/libstrongswan/credentials/cred_encoding.h b/src/libstrongswan/credentials/cred_encoding.h index b029fe2ac..41481f376 100644 --- a/src/libstrongswan/credentials/cred_encoding.h +++ b/src/libstrongswan/credentials/cred_encoding.h @@ -85,6 +85,8 @@ enum cred_encoding_type_t { /** PGP key encoding */ PUBKEY_PGP, PRIVKEY_PGP, + /** DNSKEY encoding */ + PUBKEY_DNSKEY, /** ASN.1 DER encoded certificate */ CERT_ASN1_DER, diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index 08b226468..12db0961b 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -451,7 +451,10 @@ METHOD(crypto_tester_t, test_aead, bool, failure: aead->destroy(aead); chunk_free(&cipher); - chunk_free(&plain); + if (plain.ptr != vector->plain) + { + chunk_free(&plain); + } if (failed) { DBG1(DBG_LIB, "disabled %N[%s]: %s test vector failed", diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index 30a7774df..819c6808e 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -91,6 +91,7 @@ void library_deinit() this->public.crypto->destroy(this->public.crypto); this->public.proposal->destroy(this->public.proposal); this->public.fetcher->destroy(this->public.fetcher); + this->public.resolver->destroy(this->public.resolver); this->public.db->destroy(this->public.db); this->public.printf_hook->destroy(this->public.printf_hook); this->objects->destroy(this->objects); @@ -214,6 +215,7 @@ bool library_init(char *settings) this->public.credmgr = credential_manager_create(); this->public.encoding = cred_encoding_create(); this->public.fetcher = fetcher_manager_create(); + this->public.resolver = resolver_manager_create(); this->public.db = database_factory_create(); this->public.processor = processor_create(); this->public.scheduler = scheduler_create(); diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h index f164a6052..3b6d02002 100644 --- a/src/libstrongswan/library.h +++ b/src/libstrongswan/library.h @@ -49,6 +49,9 @@ * @defgroup fetcher fetcher * @ingroup libstrongswan * + * @defgroup resolver resolver + * @ingroup libstrongswan + * * @defgroup ipsec ipsec * @ingroup libstrongswan * @@ -92,6 +95,7 @@ #include "crypto/crypto_factory.h" #include "crypto/proposal/proposal_keywords.h" #include "fetcher/fetcher_manager.h" +#include "resolver/resolver_manager.h" #include "database/database_factory.h" #include "credentials/credential_factory.h" #include "credentials/credential_manager.h" @@ -162,6 +166,11 @@ struct library_t { fetcher_manager_t *fetcher; /** + * Manager for DNS resolvers + */ + resolver_manager_t *resolver; + + /** * database construction factory */ database_factory_t *db; diff --git a/src/libstrongswan/networking/packet.c b/src/libstrongswan/networking/packet.c index a2c329d60..4ff7fc48b 100644 --- a/src/libstrongswan/networking/packet.c +++ b/src/libstrongswan/networking/packet.c @@ -39,6 +39,11 @@ struct private_packet_t { */ host_t *destination; + /** + * DSCP value on packet + */ + u_int8_t dscp; + /** * message data */ @@ -89,6 +94,17 @@ METHOD(packet_t, set_data, void, this->adjusted_data = this->data = data; } +METHOD(packet_t, get_dscp, u_int8_t, + private_packet_t *this) +{ + return this->dscp; +} +METHOD(packet_t, set_dscp, void, + private_packet_t *this, u_int8_t value) +{ + this->dscp = value; +} + METHOD(packet_t, skip_bytes, void, private_packet_t *this, size_t bytes) { @@ -123,6 +139,7 @@ METHOD(packet_t, clone_, packet_t*, { other->set_data(other, chunk_clone(this->adjusted_data)); } + other->set_dscp(other, this->dscp); return other; } @@ -141,6 +158,8 @@ packet_t *packet_create_from_data(host_t *src, host_t *dst, chunk_t data) .get_source = _get_source, .set_destination = _set_destination, .get_destination = _get_destination, + .get_dscp = _get_dscp, + .set_dscp = _set_dscp, .skip_bytes = _skip_bytes, .clone = _clone_, .destroy = _destroy, diff --git a/src/libstrongswan/networking/packet.h b/src/libstrongswan/networking/packet.h index 6fb9cece2..a96a4b84f 100644 --- a/src/libstrongswan/networking/packet.h +++ b/src/libstrongswan/networking/packet.h @@ -76,6 +76,20 @@ struct packet_t { void (*set_data)(packet_t *packet, chunk_t data); /** + * Get the DiffServ Code Point set on this packet. + * + * @return DSCP value + */ + u_int8_t (*get_dscp)(packet_t *this); + + /** + * Set the DiffServ Code Point to use on this packet. + * + * @param value DSCP value + */ + void (*set_dscp)(packet_t *this, u_int8_t value); + + /** * Increase the offset where the actual packet data starts. * * The total offset applies to future calls of get_data() and clone(). diff --git a/src/libstrongswan/networking/tun_device.c b/src/libstrongswan/networking/tun_device.c index d07327e5c..1da87df05 100644 --- a/src/libstrongswan/networking/tun_device.c +++ b/src/libstrongswan/networking/tun_device.c @@ -88,7 +88,6 @@ static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask) case AF_INET: { struct sockaddr_in *addr = (struct sockaddr_in*)&ifr->ifr_addr; - addr->sin_family = AF_INET; target = (char*)&addr->sin_addr; len = 4; break; @@ -96,7 +95,6 @@ static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask) case AF_INET6: { struct sockaddr_in6 *addr = (struct sockaddr_in6*)&ifr->ifr_addr; - addr->sin6_family = AF_INET6; target = (char*)&addr->sin6_addr; len = 16; break; @@ -105,6 +103,8 @@ static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask) return; } + ifr->ifr_addr.sa_family = family; + bytes = (netmask + 7) / 8; bits = (bytes * 8) - netmask; diff --git a/src/libstrongswan/pen/pen.c b/src/libstrongswan/pen/pen.c index b1b0731d4..f6997d1d1 100644 --- a/src/libstrongswan/pen/pen.c +++ b/src/libstrongswan/pen/pen.c @@ -22,7 +22,7 @@ ENUM_NEXT(pen_names, PEN_IBM, PEN_IBM, PEN_IETF, ENUM_NEXT(pen_names, PEN_MICROSOFT, PEN_MICROSOFT, PEN_IBM, "Microsoft"); ENUM_NEXT(pen_names, PEN_REDHAT, PEN_REDHAT, PEN_MICROSOFT, - "Redhat"); + "Redhat"); ENUM_NEXT(pen_names, PEN_OSC, PEN_OSC, PEN_REDHAT, "OSC"); ENUM_NEXT(pen_names, PEN_DEBIAN, PEN_DEBIAN, PEN_OSC, @@ -32,7 +32,7 @@ ENUM_NEXT(pen_names, PEN_GOOGLE, PEN_GOOGLE, PEN_DEBIAN, ENUM_NEXT(pen_names, PEN_TCG, PEN_TCG, PEN_GOOGLE, "TCG"); ENUM_NEXT(pen_names, PEN_CANONICAL, PEN_CANONICAL, PEN_TCG, - "Canonical"); + "Canonical"); ENUM_NEXT(pen_names, PEN_FEDORA, PEN_FEDORA, PEN_CANONICAL, "Fedora Project"); ENUM_NEXT(pen_names, PEN_FHH, PEN_FHH, PEN_FEDORA, @@ -41,7 +41,7 @@ ENUM_NEXT(pen_names, PEN_ITA, PEN_ITA, PEN_FHH, "ITA-HSR"); ENUM_NEXT(pen_names, PEN_OPENPTS, PEN_OPENPTS, PEN_ITA, "OpenPTS"); -ENUM_NEXT(pen_names, PEN_RESERVED, PEN_RESERVED, PEN_OPENPTS, +ENUM_NEXT(pen_names, PEN_UNASSIGNED, PEN_RESERVED, PEN_OPENPTS, + "Unassigned", "Reserved"); ENUM_END(pen_names, PEN_RESERVED); - diff --git a/src/libstrongswan/pen/pen.h b/src/libstrongswan/pen/pen.h index 9d5df7d49..29e312c61 100644 --- a/src/libstrongswan/pen/pen.h +++ b/src/libstrongswan/pen/pen.h @@ -29,25 +29,31 @@ typedef enum pen_t pen_t; typedef struct pen_type_t pen_type_t; +/** + * Private enterprise numbers allocated by IANA. + * + * http://www.iana.org/assignments/enterprise-numbers + */ enum pen_t { - PEN_IETF = 0x000000, /* 0 */ - PEN_IBM = 0x000002, /* 2 */ - PEN_MICROSOFT = 0x000137, /* 311 */ - PEN_REDHAT = 0x000908, /* 2312 */ - PEN_OSC = 0x002358, /* 9048 */ - PEN_DEBIAN = 0x002572, /* 9586 */ - PEN_GOOGLE = 0x002B79, /* 11129 */ - PEN_TCG = 0x005597, /* 21911 */ - PEN_CANONICAL = 0x007132, /* 28978 */ - PEN_FEDORA = 0x0076C1, /* 30401 */ - PEN_FHH = 0x0080ab, /* 32939 */ - PEN_ITA = 0x00902a, /* 36906 */ - PEN_OPENPTS = 0x00950e, /* 38158 */ - PEN_RESERVED = 0xffffff, /* 16777215 */ + PEN_IETF = 0x000000, /* 0 */ + PEN_IBM = 0x000002, /* 2 */ + PEN_MICROSOFT = 0x000137, /* 311 */ + PEN_REDHAT = 0x000908, /* 2312 */ + PEN_OSC = 0x002358, /* 9048 */ + PEN_DEBIAN = 0x002572, /* 9586 */ + PEN_GOOGLE = 0x002B79, /* 11129 */ + PEN_TCG = 0x005597, /* 21911 */ + PEN_CANONICAL = 0x007132, /* 28978 */ + PEN_FEDORA = 0x0076C1, /* 30401 */ + PEN_FHH = 0x0080ab, /* 32939 */ + PEN_ITA = 0x00902a, /* 36906 */ + PEN_OPENPTS = 0x00950e, /* 38158 */ + PEN_UNASSIGNED = 0xfffffe, /* 16777214 */ + PEN_RESERVED = 0xffffff, /* 16777215 */ }; /** - * Vendor specific type + * Vendor specific type in vendor specific namespace. */ struct pen_type_t { pen_t vendor_id; @@ -56,14 +62,44 @@ struct pen_type_t { /** * Create a pen_type_t struct + * + * @param vendor_id vendor ID to create a pen_type_t + * @param type type to create a pen_type_t + * @return created pen_type_t */ static inline pen_type_t pen_type_create(pen_t vendor_id, u_int32_t type) { - pen_type_t pen_type = {vendor_id, type}; + pen_type_t pen_type = { vendor_id, type }; return pen_type; } /** + * Check two pen_type_t for equality. + * + * @param a first pen_type_t to compare + * @param b second pen_type_t to compare + * @return TRUE if a == b + */ +static inline bool pen_type_equals(pen_type_t a, pen_type_t b) +{ + return a.vendor_id == b.vendor_id && a.type == b.type; +} + +/** + * Check if a pen_type_t matches vendor and type. + * + * @param pen_type pen_type_t to compare + * @param vendor_id vendor to check in pen_type + * @param type type to check in pen_type + * @return TRUE if vendor_id and type matches pen_type + */ +static inline bool pen_type_is(pen_type_t pen_type, + pen_t vendor_id, u_int32_t type) +{ + return pen_type.vendor_id == vendor_id && pen_type.type == type; +} + +/** * enum names for pen_t. */ extern enum_name_t *pen_names; diff --git a/src/libstrongswan/plugins/ccm/ccm_aead.h b/src/libstrongswan/plugins/ccm/ccm_aead.h index d5e302f94..79ab31804 100644 --- a/src/libstrongswan/plugins/ccm/ccm_aead.h +++ b/src/libstrongswan/plugins/ccm/ccm_aead.h @@ -42,8 +42,8 @@ struct ccm_aead_t { /** * Create a ccm_aead instance. * - * @param key_size key size in bytes * @param algo algorithm to implement, a CCM mode + * @param key_size key size in bytes * @return aead, NULL if not supported */ ccm_aead_t *ccm_aead_create(encryption_algorithm_t algo, size_t key_size); diff --git a/src/libstrongswan/plugins/curl/curl_fetcher.c b/src/libstrongswan/plugins/curl/curl_fetcher.c index c68b74f96..b49961a90 100644 --- a/src/libstrongswan/plugins/curl/curl_fetcher.c +++ b/src/libstrongswan/plugins/curl/curl_fetcher.c @@ -21,7 +21,7 @@ #include "curl_fetcher.h" -#define DEFAULT_TIMEOUT 10 +#define CONNECT_TIMEOUT 10 typedef struct private_curl_fetcher_t private_curl_fetcher_t; @@ -48,6 +48,11 @@ struct private_curl_fetcher_t { * Callback function */ fetcher_callback_t cb; + + /** + * Timeout for a transfer + */ + long timeout; }; /** @@ -94,7 +99,11 @@ METHOD(fetcher_t, fetch, status_t, curl_easy_setopt(this->curl, CURLOPT_ERRORBUFFER, error); curl_easy_setopt(this->curl, CURLOPT_FAILONERROR, TRUE); curl_easy_setopt(this->curl, CURLOPT_NOSIGNAL, TRUE); - curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_TIMEOUT); + if (this->timeout) + { + curl_easy_setopt(this->curl, CURLOPT_TIMEOUT, this->timeout); + } + curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT); curl_easy_setopt(this->curl, CURLOPT_WRITEFUNCTION, (void*)curl_cb); curl_easy_setopt(this->curl, CURLOPT_WRITEDATA, &data); if (this->headers) @@ -160,8 +169,7 @@ METHOD(fetcher_t, set_option, bool, } case FETCH_TIMEOUT: { - curl_easy_setopt(this->curl, CURLOPT_CONNECTTIMEOUT, - va_arg(args, u_int)); + this->timeout = va_arg(args, u_int); break; } case FETCH_CALLBACK: @@ -211,4 +219,3 @@ curl_fetcher_t *curl_fetcher_create() } return &this->public; } - diff --git a/src/libstrongswan/plugins/dnskey/Makefile.am b/src/libstrongswan/plugins/dnskey/Makefile.am index fbba95e0a..0f2e554c1 100644 --- a/src/libstrongswan/plugins/dnskey/Makefile.am +++ b/src/libstrongswan/plugins/dnskey/Makefile.am @@ -11,6 +11,7 @@ endif libstrongswan_dnskey_la_SOURCES = \ dnskey_plugin.h dnskey_plugin.c \ - dnskey_builder.h dnskey_builder.c + dnskey_builder.h dnskey_builder.c \ + dnskey_encoder.h dnskey_encoder.c libstrongswan_dnskey_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/dnskey/dnskey_builder.c b/src/libstrongswan/plugins/dnskey/dnskey_builder.c index b8a451500..71040437d 100644 --- a/src/libstrongswan/plugins/dnskey/dnskey_builder.c +++ b/src/libstrongswan/plugins/dnskey/dnskey_builder.c @@ -39,8 +39,14 @@ enum dnskey_algorithm_t { DNSKEY_ALG_RSA_MD5 = 1, DNSKEY_ALG_DH = 2, DNSKEY_ALG_DSA = 3, - DNSKEY_ALG_ECC = 4, DNSKEY_ALG_RSA_SHA1 = 5, + DNSKEY_ALG_DSA_NSEC3_SHA1 = 6, + DNSKEY_ALG_RSA_SHA1_NSEC3_SHA1 = 7, + DNSKEY_ALG_RSA_SHA256 = 8, + DNSKEY_ALG_RSA_SHA512 = 10, + DNSKEY_ALG_ECC_GOST = 12, + DNSKEY_ALG_ECDSA_P256_SHA256 = 13, + DNSKEY_ALG_ECDSA_P384_SHA384 = 14 }; /** @@ -59,7 +65,11 @@ static dnskey_public_key_t *parse_public_key(chunk_t blob) switch (rr->algorithm) { + case DNSKEY_ALG_RSA_MD5: case DNSKEY_ALG_RSA_SHA1: + case DNSKEY_ALG_RSA_SHA1_NSEC3_SHA1: + case DNSKEY_ALG_RSA_SHA256: + case DNSKEY_ALG_RSA_SHA512: return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, BUILD_BLOB_DNSKEY, blob, BUILD_END); default: diff --git a/src/libstrongswan/plugins/dnskey/dnskey_encoder.c b/src/libstrongswan/plugins/dnskey/dnskey_encoder.c new file mode 100644 index 000000000..d2b9894b8 --- /dev/null +++ b/src/libstrongswan/plugins/dnskey/dnskey_encoder.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2013 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. + */ + +#include "dnskey_encoder.h" + +#include <utils/debug.h> + +/** + * Encode an RSA public key in DNSKEY format (RFC 3110) + */ +bool build_pub(chunk_t *encoding, va_list args) +{ + chunk_t n, e, pubkey; + size_t exp_len; + u_char *pos; + + if (cred_encoding_args(args, CRED_PART_RSA_MODULUS, &n, + CRED_PART_RSA_PUB_EXP, &e, CRED_PART_END)) + { + /* remove leading zeros in exponent and modulus */ + while (*e.ptr == 0) + { + e = chunk_skip(e, 1); + } + while (*n.ptr == 0) + { + n = chunk_skip(n, 1); + } + + if (e.len < 256) + { + /* exponent length fits into a single octet */ + exp_len = 1; + pubkey = chunk_alloc(exp_len + e.len + n.len); + pubkey.ptr[0] = (char)e.len; + } + else if (e.len < 65536) + { + /* exponent length fits into two octets preceded by zero octet */ + exp_len = 3; + pubkey = chunk_alloc(exp_len + e.len + n.len); + pubkey.ptr[0] = 0x00; + htoun16(pubkey.ptr + 1, e.len); + } + else + { + /* exponent length is too large */ + return FALSE; + } + + /* copy exponent and modulus and convert to base64 format */ + pos = pubkey.ptr + exp_len; + memcpy(pos, e.ptr, e.len); + pos += e.len; + memcpy(pos, n.ptr, n.len); + *encoding = chunk_to_base64(pubkey, NULL); + chunk_free(&pubkey); + + return TRUE; + } + return FALSE; +} + +/** + * See header. + */ +bool dnskey_encoder_encode(cred_encoding_type_t type, chunk_t *encoding, + va_list args) +{ + switch (type) + { + case PUBKEY_DNSKEY: + return build_pub(encoding, args); + default: + return FALSE; + } +} + + diff --git a/src/libstrongswan/plugins/dnskey/dnskey_encoder.h b/src/libstrongswan/plugins/dnskey/dnskey_encoder.h new file mode 100644 index 000000000..698d29301 --- /dev/null +++ b/src/libstrongswan/plugins/dnskey/dnskey_encoder.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 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 dnskey_encoder dnskey_encoder + * @{ @ingroup dnskey + */ + +#ifndef DNSKEY_ENCODER_H_ +#define DNSKEY_ENCODER_H_ + +#include <credentials/cred_encoding.h> + +/** + * Encoding function for DNSKEY (RFC 3110) public key format. + */ +bool dnskey_encoder_encode(cred_encoding_type_t type, chunk_t *encoding, + va_list args); + +#endif /** DNSKEY_ENCODER_H_ @}*/ diff --git a/src/libstrongswan/plugins/dnskey/dnskey_plugin.c b/src/libstrongswan/plugins/dnskey/dnskey_plugin.c index b6863e8e3..9a4f6252f 100644 --- a/src/libstrongswan/plugins/dnskey/dnskey_plugin.c +++ b/src/libstrongswan/plugins/dnskey/dnskey_plugin.c @@ -17,6 +17,7 @@ #include <library.h> #include "dnskey_builder.h" +#include "dnskey_encoder.h" typedef struct private_dnskey_plugin_t private_dnskey_plugin_t; @@ -53,6 +54,8 @@ METHOD(plugin_t, get_features, int, METHOD(plugin_t, destroy, void, private_dnskey_plugin_t *this) { + lib->encoding->remove_encoder(lib->encoding, dnskey_encoder_encode); + free(this); } @@ -73,6 +76,8 @@ plugin_t *dnskey_plugin_create() }, ); + lib->encoding->add_encoder(lib->encoding, dnskey_encoder_encode); + return &this->public.plugin; } diff --git a/src/libstrongswan/plugins/gcm/gcm_aead.h b/src/libstrongswan/plugins/gcm/gcm_aead.h index db4be2442..846c3c76c 100644 --- a/src/libstrongswan/plugins/gcm/gcm_aead.h +++ b/src/libstrongswan/plugins/gcm/gcm_aead.h @@ -42,8 +42,8 @@ struct gcm_aead_t { /** * Create a gcm_aead instance. * - * @param key_size key size in bytes * @param algo algorithm to implement, a gcm mode + * @param key_size key size in bytes * @return aead, NULL if not supported */ gcm_aead_t *gcm_aead_create(encryption_algorithm_t algo, size_t key_size); diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index f971a5e08..e71567311 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -25,7 +25,8 @@ libstrongswan_openssl_la_SOURCES = \ openssl_crl.c openssl_crl.h \ openssl_pkcs7.c openssl_pkcs7.h \ openssl_rng.c openssl_rng.h \ - openssl_hmac.c openssl_hmac.h + openssl_hmac.c openssl_hmac.h \ + openssl_gcm.c openssl_gcm.h libstrongswan_openssl_la_LDFLAGS = -module -avoid-version libstrongswan_openssl_la_LIBADD = -lcrypto diff --git a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c index 4dc5663f1..ff3382473 100644 --- a/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c +++ b/src/libstrongswan/plugins/openssl/openssl_diffie_hellman.c @@ -14,6 +14,10 @@ * for more details. */ +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_DH + #include <openssl/dh.h> #include "openssl_diffie_hellman.h" @@ -193,3 +197,5 @@ openssl_diffie_hellman_t *openssl_diffie_hellman_create( return &this->public; } + +#endif /* OPENSSL_NO_DH */ diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c index d350d050b..12f264267 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c @@ -16,7 +16,7 @@ #include <openssl/opensslconf.h> -#ifndef OPENSSL_NO_EC +#ifndef OPENSSL_NO_ECDSA #include "openssl_ec_private_key.h" #include "openssl_ec_public_key.h" @@ -423,5 +423,4 @@ error: destroy(this); return NULL; } -#endif /* OPENSSL_NO_EC */ - +#endif /* OPENSSL_NO_ECDSA */ diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c index 3f5125b31..c8a45f79a 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c @@ -16,7 +16,7 @@ #include <openssl/opensslconf.h> -#ifndef OPENSSL_NO_EC +#ifndef OPENSSL_NO_ECDSA #include "openssl_ec_public_key.h" #include "openssl_util.h" @@ -360,5 +360,5 @@ openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type, } return &this->public; } -#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_NO_ECDSA */ diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.c b/src/libstrongswan/plugins/openssl/openssl_gcm.c new file mode 100644 index 000000000..fde7ae71d --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_gcm.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2013 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 "openssl_gcm.h" + +#include <openssl/evp.h> + +/** as defined in RFC 4106 */ +#define IV_LEN 8 +#define SALT_LEN 4 +#define NONCE_LEN (IV_LEN + SALT_LEN) + +typedef struct private_aead_t private_aead_t; + +/** + * Private data of aead_t + */ +struct private_aead_t { + + /** + * Public interface + */ + aead_t public; + + /** + * The encryption key + */ + chunk_t key; + + /** + * Salt value + */ + char salt[SALT_LEN]; + + /** + * Size of the integrity check value + */ + size_t icv_size; + + /** + * The cipher to use + */ + const EVP_CIPHER *cipher; +}; + +/** + * Do the actual en/decryption in an EVP context + */ +static bool crypt(private_aead_t *this, chunk_t data, chunk_t assoc, chunk_t iv, + u_char *out, int enc) +{ + EVP_CIPHER_CTX ctx; + u_char nonce[NONCE_LEN]; + bool success = FALSE; + int len; + + memcpy(nonce, this->salt, SALT_LEN); + memcpy(nonce + SALT_LEN, iv.ptr, IV_LEN); + + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + if (!EVP_CipherInit_ex(&ctx, this->cipher, NULL, NULL, NULL, enc) || + !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, NONCE_LEN, NULL) || + !EVP_CipherInit_ex(&ctx, NULL, NULL, this->key.ptr, nonce, enc)) + { + goto done; + } + if (!enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, this->icv_size, + data.ptr + data.len)) + { /* set ICV for verification on decryption */ + goto done; + } + if (assoc.len && !EVP_CipherUpdate(&ctx, NULL, &len, assoc.ptr, assoc.len)) + { /* set AAD if specified */ + goto done; + } + if (!EVP_CipherUpdate(&ctx, out, &len, data.ptr, data.len) || + !EVP_CipherFinal_ex(&ctx, out + len, &len)) + { /* EVP_CipherFinal_ex fails if ICV is incorrect on decryption */ + goto done; + } + if (enc && !EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, this->icv_size, + out + data.len)) + { /* copy back the ICV when encrypting */ + goto done; + } + success = TRUE; + +done: + EVP_CIPHER_CTX_cleanup(&ctx); + return success; +} + +METHOD(aead_t, encrypt, bool, + private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv, + chunk_t *encrypted) +{ + u_char *out; + + out = plain.ptr; + if (encrypted) + { + *encrypted = chunk_alloc(plain.len + this->icv_size); + out = encrypted->ptr; + } + return crypt(this, plain, assoc, iv, out, 1); +} + +METHOD(aead_t, decrypt, bool, + private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv, + chunk_t *plain) +{ + u_char *out; + + if (encrypted.len < this->icv_size) + { + return FALSE; + } + encrypted.len -= this->icv_size; + + out = encrypted.ptr; + if (plain) + { + *plain = chunk_alloc(encrypted.len); + out = plain->ptr; + } + return crypt(this, encrypted, assoc, iv, out, 0); +} + +METHOD(aead_t, get_block_size, size_t, + private_aead_t *this) +{ + return this->cipher->block_size; +} + +METHOD(aead_t, get_icv_size, size_t, + private_aead_t *this) +{ + return this->icv_size; +} + +METHOD(aead_t, get_iv_size, size_t, + private_aead_t *this) +{ + return IV_LEN; +} + +METHOD(aead_t, get_key_size, size_t, + private_aead_t *this) +{ + return this->key.len + SALT_LEN; +} + +METHOD(aead_t, set_key, bool, + private_aead_t *this, chunk_t key) +{ + if (key.len != get_key_size(this)) + { + return FALSE; + } + memcpy(this->salt, key.ptr + key.len - SALT_LEN, SALT_LEN); + memcpy(this->key.ptr, key.ptr, this->key.len); + return TRUE; +} + +METHOD(aead_t, destroy, void, + private_aead_t *this) +{ + chunk_clear(&this->key); + free(this); +} + +/* + * Described in header + */ +aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size) +{ + private_aead_t *this; + + INIT(this, + .public = { + .encrypt = _encrypt, + .decrypt = _decrypt, + .get_block_size = _get_block_size, + .get_icv_size = _get_icv_size, + .get_iv_size = _get_iv_size, + .get_key_size = _get_key_size, + .set_key = _set_key, + .destroy = _destroy, + }, + ); + + switch (algo) + { + case ENCR_AES_GCM_ICV8: + this->icv_size = 8; + break; + case ENCR_AES_GCM_ICV12: + this->icv_size = 12; + break; + case ENCR_AES_GCM_ICV16: + this->icv_size = 16; + break; + default: + free(this); + return NULL; + } + + switch (algo) + { + case ENCR_AES_GCM_ICV8: + case ENCR_AES_GCM_ICV12: + case ENCR_AES_GCM_ICV16: + switch (key_size) + { + case 0: + key_size = 16; + /* FALL */ + case 16: + this->cipher = EVP_get_cipherbyname("aes-128-gcm"); + break; + case 24: + this->cipher = EVP_get_cipherbyname("aes-192-gcm"); + break; + case 32: + this->cipher = EVP_get_cipherbyname("aes-256-gcm"); + break; + default: + free(this); + return NULL; + } + break; + default: + free(this); + return NULL; + } + + if (!this->cipher) + { + free(this); + return NULL; + } + + this->key = chunk_alloc(key_size); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/openssl/openssl_gcm.h b/src/libstrongswan/plugins/openssl/openssl_gcm.h new file mode 100644 index 000000000..12d2e8ab6 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_gcm.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 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. + */ + +/** + * Implements the aead_t interface using OpenSSL in GCM mode. + * + * @defgroup openssl_gcm openssl_gcm + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_GCM_H_ +#define OPENSSL_GCM_H_ + +#include <crypto/aead.h> + +/** + * Constructor to create aead_t implementation. + * + * @param algo algorithm to implement + * @param key_size key size in bytes + * @return aead_t object, NULL if not supported + */ +aead_t *openssl_gcm_create(encryption_algorithm_t algo, size_t key_size); + +#endif /** OPENSSL_GCM_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_hmac.c b/src/libstrongswan/plugins/openssl/openssl_hmac.c index 5d05425d3..4f0bcc7c3 100644 --- a/src/libstrongswan/plugins/openssl/openssl_hmac.c +++ b/src/libstrongswan/plugins/openssl/openssl_hmac.c @@ -35,6 +35,10 @@ * THE SOFTWARE. */ +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_HMAC + #include <openssl/evp.h> #include <openssl/hmac.h> @@ -189,3 +193,4 @@ signer_t *openssl_hmac_signer_create(integrity_algorithm_t algo) return NULL; } +#endif /* OPENSSL_NO_HMAC */ diff --git a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c index ccc426235..9c3c4040c 100644 --- a/src/libstrongswan/plugins/openssl/openssl_pkcs7.c +++ b/src/libstrongswan/plugins/openssl/openssl_pkcs7.c @@ -13,8 +13,10 @@ * for more details. */ +#include <openssl/opensslv.h> #include <openssl/opensslconf.h> +#if OPENSSL_VERSION_NUMBER >= 0x0090807fL #ifndef OPENSSL_NO_CMS #include "openssl_pkcs7.h" @@ -788,3 +790,4 @@ pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args) } #endif /* OPENSSL_NO_CMS */ +#endif /* OPENSSL_VERSION_NUMBER */ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index dd6a379d2..282fe2b1b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -43,6 +43,7 @@ #include "openssl_pkcs7.h" #include "openssl_rng.h" #include "openssl_hmac.h" +#include "openssl_gcm.h" typedef struct private_openssl_plugin_t private_openssl_plugin_t; @@ -304,6 +305,19 @@ METHOD(plugin_t, get_features, int, PLUGIN_PROVIDE(SIGNER, AUTH_HMAC_SHA2_512_256), #endif #endif /* OPENSSL_NO_HMAC */ +#ifndef OPENSSL_NO_AES + /* AES GCM */ + PLUGIN_REGISTER(AEAD, openssl_gcm_create), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV8, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV12, 32), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 16), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 24), + PLUGIN_PROVIDE(AEAD, ENCR_AES_GCM_ICV16, 32), +#endif /* OPENSSL_NO_AES */ #ifndef OPENSSL_NO_DH /* MODP DH groups */ PLUGIN_REGISTER(DH, openssl_diffie_hellman_create), @@ -366,10 +380,12 @@ METHOD(plugin_t, get_features, int, PLUGIN_SDEPEND(PUBKEY, KEY_DSA), PLUGIN_REGISTER(CERT_DECODE, openssl_crl_load, TRUE), PLUGIN_PROVIDE(CERT_DECODE, CERT_X509_CRL), +#if OPENSSL_VERSION_NUMBER >= 0x0090807fL #ifndef OPENSSL_NO_CMS PLUGIN_REGISTER(CONTAINER_DECODE, openssl_pkcs7_load, TRUE), PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS7), #endif /* OPENSSL_NO_CMS */ +#endif /* OPENSSL_VERSION_NUMBER */ #ifndef OPENSSL_NO_ECDH /* EC DH groups */ PLUGIN_REGISTER(DH, openssl_ec_diffie_hellman_create), @@ -470,4 +486,3 @@ plugin_t *openssl_plugin_create() return &this->public.plugin; } - diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c index 926e5928c..fb86a6bf1 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_private_key.c @@ -14,6 +14,10 @@ * for more details. */ +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_RSA + #include "openssl_rsa_private_key.h" #include "openssl_rsa_public_key.h" @@ -599,3 +603,4 @@ openssl_rsa_private_key_t *openssl_rsa_private_key_connect(key_type_t type, #endif /* OPENSSL_NO_ENGINE */ } +#endif /* OPENSSL_NO_RSA */ diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c index 0da5d2514..bf71d7901 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c @@ -14,6 +14,10 @@ * for more details. */ +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_RSA + #include "openssl_rsa_public_key.h" #include <utils/debug.h> @@ -388,3 +392,5 @@ openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type, destroy(this); return NULL; } + +#endif /* OPENSSL_NO_RSA */ diff --git a/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c b/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c index 8501e2cd4..8c00e6a57 100644 --- a/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c +++ b/src/libstrongswan/plugins/openssl/openssl_sha1_prf.c @@ -13,6 +13,10 @@ * for more details. */ +#include <openssl/opensslconf.h> + +#ifndef OPENSSL_NO_SHA1 + #include "openssl_sha1_prf.h" #include <openssl/sha.h> @@ -143,3 +147,4 @@ openssl_sha1_prf_t *openssl_sha1_prf_create(pseudo_random_function_t algo) return &this->public; } +#endif /* OPENSSL_NO_SHA1 */ diff --git a/src/libstrongswan/plugins/rdrand/rdrand_rng.h b/src/libstrongswan/plugins/rdrand/rdrand_rng.h index d15a48224..3fb49ce6e 100644 --- a/src/libstrongswan/plugins/rdrand/rdrand_rng.h +++ b/src/libstrongswan/plugins/rdrand/rdrand_rng.h @@ -15,7 +15,7 @@ /** * @defgroup rdrand_rng rdrand_rng - * @{ @ingroup rdrand + * @{ @ingroup rdrand_p */ #ifndef RDRAND_RNG_H_ diff --git a/src/libstrongswan/plugins/unbound/Makefile.am b/src/libstrongswan/plugins/unbound/Makefile.am new file mode 100644 index 000000000..efb313407 --- /dev/null +++ b/src/libstrongswan/plugins/unbound/Makefile.am @@ -0,0 +1,20 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${sysconfdir}\" + + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-unbound.la +else +plugin_LTLIBRARIES = libstrongswan-unbound.la +endif + +libstrongswan_unbound_la_SOURCES = \ + unbound_plugin.h unbound_plugin.c \ + unbound_resolver.c unbound_resolver.h \ + unbound_rr.h unbound_rr.c \ + unbound_response.h unbound_response.c + +libstrongswan_unbound_la_LDFLAGS = -module -avoid-version +libstrongswan_unbound_la_LIBADD = -lunbound -lldns diff --git a/src/libstrongswan/plugins/unbound/unbound_plugin.c b/src/libstrongswan/plugins/unbound/unbound_plugin.c new file mode 100644 index 000000000..90b95330a --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_plugin.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 "unbound_plugin.h" + +#include <library.h> +#include "unbound_resolver.h" + +typedef struct private_unbound_plugin_t private_unbound_plugin_t; + +/** + * private data of unbound_plugin + */ +struct private_unbound_plugin_t { + + /** + * public functions + */ + unbound_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_unbound_plugin_t *this) +{ + return "unbound"; +} + +METHOD(plugin_t, destroy, void, + private_unbound_plugin_t *this) +{ + lib->resolver->remove_resolver(lib->resolver, unbound_resolver_create); + free(this); +} + +/* + * see header file + */ +plugin_t *unbound_plugin_create() +{ + private_unbound_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .destroy = _destroy, + }, + }, + ); + + lib->resolver->add_resolver(lib->resolver, unbound_resolver_create); + + return &this->public.plugin; +} diff --git a/src/libstrongswan/plugins/unbound/unbound_plugin.h b/src/libstrongswan/plugins/unbound/unbound_plugin.h new file mode 100644 index 000000000..1f0d36454 --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 unbound_p unbound + * @ingroup plugins + * + * @defgroup unbound_plugin unbound_plugin + * @{ @ingroup unbound_p + */ + +#ifndef unbound_PLUGIN_H_ +#define unbound_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct unbound_plugin_t unbound_plugin_t; + +/** + * Plugin implementing the resolver interface using the libunbound DNS library. + */ +struct unbound_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** unbound_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/unbound/unbound_resolver.c b/src/libstrongswan/plugins/unbound/unbound_resolver.c new file mode 100644 index 000000000..44a2c764b --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_resolver.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 <unbound.h> +#include <errno.h> +#include <ldns/ldns.h> +#include <string.h> + +#include <library.h> +#include <utils/debug.h> + +#include "unbound_resolver.h" +#include "unbound_response.h" + +/* DNS resolver configuration and DNSSEC trust anchors */ +#define RESOLV_CONF_FILE "/etc/resolv.conf" +#define TRUST_ANCHOR_FILE IPSEC_CONFDIR "/ipsec.d/dnssec.keys" + +typedef struct private_resolver_t private_resolver_t; + +/** + * private data of a unbound_resolver_t object. + */ +struct private_resolver_t { + + /** + * Public data + */ + resolver_t public; + + /** + * private unbound resolver handle (unbound context) + */ + struct ub_ctx *ctx; +}; + +/** + * query method implementation + */ +METHOD(resolver_t, query, resolver_response_t*, + private_resolver_t *this, char *domain, rr_class_t rr_class, + rr_type_t rr_type) +{ + unbound_response_t *response = NULL; + struct ub_result *result = NULL; + int ub_retval; + + ub_retval = ub_resolve(this->ctx, domain, rr_type, rr_class, &result); + if (ub_retval) + { + DBG1(DBG_LIB, "unbound resolver error: %s", ub_strerror(ub_retval)); + ub_resolve_free(result); + return NULL; + } + + response = unbound_response_create_frm_libub_response(result); + if (!response) + { + DBG1(DBG_LIB, "unbound resolver failed to create response"); + ub_resolve_free(result); + return NULL; + } + ub_resolve_free(result); + + return (resolver_response_t*)response; +} + +/** + * destroy method implementation + */ +METHOD(resolver_t, destroy, void, + private_resolver_t *this) +{ + ub_ctx_delete(this->ctx); + free(this); +} + +/* + * Described in header. + */ +resolver_t *unbound_resolver_create(void) +{ + private_resolver_t *this; + int ub_retval = 0; + char *resolv_conf_file; + char *trust_anchor_file; + + resolv_conf_file = lib->settings->get_str(lib->settings, + "libstrongswan.plugins.unbound.resolv_conf", + RESOLV_CONF_FILE); + + trust_anchor_file = lib->settings->get_str(lib->settings, + "libstrongswan.plugins.unbound.trust_anchors", + TRUST_ANCHOR_FILE); + + INIT(this, + .public = { + .query = _query, + .destroy = _destroy, + }, + ); + + this->ctx = ub_ctx_create(); + if (!this->ctx) + { + DBG1(DBG_LIB, "failed to create unbound resolver context"); + destroy(this); + return NULL; + } + + DBG1(DBG_CFG, "loading unbound resolver config from '%s'", resolv_conf_file); + ub_retval = ub_ctx_resolvconf(this->ctx, resolv_conf_file); + if (ub_retval) + { + DBG1(DBG_CFG, "failed to read the resolver config: %s (%s)", + ub_strerror(ub_retval), strerror(errno)); + destroy(this); + return NULL; + } + + DBG1(DBG_CFG, "loading unbound trust anchors from '%s'", trust_anchor_file); + ub_retval = ub_ctx_add_ta_file(this->ctx, trust_anchor_file); + if (ub_retval) + { + DBG1(DBG_CFG, "failed to load trust anchors: %s (%s)", + ub_strerror(ub_retval), strerror(errno)); + } + + return &this->public; +} + diff --git a/src/libstrongswan/plugins/unbound/unbound_resolver.h b/src/libstrongswan/plugins/unbound/unbound_resolver.h new file mode 100644 index 000000000..818a717b8 --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_resolver.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 unbound_resolver unbound_resolver + * @{ @ingroup unbound_p + */ + +#ifndef unbound_RESOLVER_H_ +#define unbound_RESOLVER_H_ + +/** + * Create a resolver_t instance. + */ +resolver_t *unbound_resolver_create(void); + +#endif /** LIBunbound_RESOLVER_H_ @}*/ diff --git a/src/libstrongswan/plugins/unbound/unbound_response.c b/src/libstrongswan/plugins/unbound/unbound_response.c new file mode 100644 index 000000000..6f6c25e89 --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_response.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 <resolver/resolver_response.h> +#include <resolver/rr.h> +#include "unbound_rr.h" +#include "unbound_response.h" + +#include <library.h> +#include <utils/debug.h> + +#include <unbound.h> +#include <ldns/ldns.h> + +typedef struct private_unbound_response_t private_unbound_response_t; + +/** + * private data of an unbound_response_t object. + */ +struct private_unbound_response_t { + + /** + * Public data + */ + unbound_response_t public; + + /** + * Original question string + */ + char* query_name; + + /** + * Canonical name of the response + */ + char* canon_name; + + /** + * Are the some RRs in the RRset of this response? + */ + bool has_data; + + /* + * Does the queried name exist? + */ + bool query_name_exist; + + /** + * DNSSEC security state + */ + dnssec_status_t security_state; + + /** + * RRset + */ + rr_set_t *rr_set; +}; + +METHOD(resolver_response_t, get_query_name, char*, + private_unbound_response_t *this) +{ + return this->query_name; +} + +METHOD(resolver_response_t, get_canon_name, char*, + private_unbound_response_t *this) +{ + return this->canon_name; +} + +METHOD(resolver_response_t, has_data, bool, + private_unbound_response_t *this) +{ + return this->has_data; +} + +METHOD(resolver_response_t, query_name_exist, bool, + private_unbound_response_t *this) +{ + return this->query_name_exist; +} + +METHOD(resolver_response_t, get_security_state, dnssec_status_t, + private_unbound_response_t *this) +{ + return this->security_state; +} + +METHOD(resolver_response_t, get_rr_set, rr_set_t*, + private_unbound_response_t *this) +{ + return this->rr_set; +} + +METHOD(resolver_response_t, destroy, void, + private_unbound_response_t *this) +{ + free(this->query_name); + free(this->canon_name); + DESTROY_IF(this->rr_set); + free(this); +} + +/* + * Described in header. + */ +unbound_response_t *unbound_response_create_frm_libub_response( + struct ub_result *libub_response) +{ + private_unbound_response_t *this = NULL; + + INIT(this, + .public = { + .interface = { + .get_query_name = _get_query_name, + .get_canon_name = _get_canon_name, + .has_data = _has_data, + .query_name_exist = _query_name_exist, + .get_security_state = _get_security_state, + .get_rr_set = _get_rr_set, + .destroy = _destroy, + }, + }, + ); + + this->query_name = strdup(libub_response->qname); + + if (libub_response->canonname) + { + this->canon_name = strdup(libub_response->canonname); + } + + this->has_data = libub_response->havedata; + + this->query_name_exist = !(libub_response->nxdomain); + + if (libub_response->secure) + { + this->security_state = SECURE; + } + else if (libub_response->bogus) + { + this->security_state = BOGUS; + } + else + { + this->security_state = INDETERMINATE; + } + + /** + * Create RRset + */ + if (this->query_name_exist && this->has_data) + { + ldns_pkt *dns_pkt = NULL; + ldns_rr_list *orig_rr_list = NULL; + size_t orig_rr_count; + ldns_rr *orig_rr = NULL; + ldns_rdf *orig_rdf = NULL; + ldns_status status; + linked_list_t *rr_list = NULL, *rrsig_list = NULL; + unbound_rr_t *rr = NULL; + int i; + + /**Parse the received DNS packet using the ldns library */ + status = ldns_wire2pkt(&dns_pkt, libub_response->answer_packet, + libub_response->answer_len); + + if (status != LDNS_STATUS_OK) + { + DBG1(DBG_LIB, "failed to parse DNS packet"); + destroy(this); + return NULL; + } + + /* Create a list with the queried RRs. If there are corresponding RRSIGs + * create also a list with these. + */ + rr_list = linked_list_create(); + + orig_rr_list = ldns_pkt_get_section_clone(dns_pkt, LDNS_SECTION_ANSWER); + orig_rr_count = ldns_rr_list_rr_count(orig_rr_list); + + for (i = 0; i < orig_rr_count; i++) + { + orig_rr = ldns_rr_list_rr(orig_rr_list, i); + + if (ldns_rr_get_type(orig_rr) == libub_response->qtype && + ldns_rr_get_class(orig_rr) == libub_response->qclass) + { + /* RR is part of the queried RRset. + * => add it to the list of Resource Records. + */ + rr = unbound_rr_create_frm_ldns_rr(orig_rr); + if (rr) + { + rr_list->insert_last(rr_list, rr); + } + else + { + DBG1(DBG_LIB, "failed to create RR"); + } + } + + if (ldns_rr_get_type(orig_rr) == LDNS_RR_TYPE_RRSIG) + { + orig_rdf = ldns_rr_rrsig_typecovered(orig_rr); + if (!orig_rdf) + { + DBG1(DBG_LIB, "failed to get the type covered by an RRSIG"); + } + else if (ldns_rdf2native_int16(orig_rdf) == libub_response->qtype) + { + /* The current RR represent a signature (RRSIG) + * which belongs to the queried RRset. + * => add it to the list of signatures. + */ + rr = unbound_rr_create_frm_ldns_rr(orig_rr); + if (rr) + { + if (!rrsig_list) + { + rrsig_list = linked_list_create(); + } + rrsig_list->insert_last(rrsig_list, rr); + } + else + { + DBG1(DBG_LIB, "failed to create RRSIG"); + } + } + else + { + DBG1(DBG_LIB, "failed to determine the RR type " + "covered by RRSIG RR"); + } + } + } + /** + * Create the RRset for which the query was performed. + */ + this->rr_set = rr_set_create(rr_list, rrsig_list); + + ldns_pkt_free(dns_pkt); + ldns_rr_list_free(orig_rr_list); + } + return &this->public; +} diff --git a/src/libstrongswan/plugins/unbound/unbound_response.h b/src/libstrongswan/plugins/unbound/unbound_response.h new file mode 100644 index 000000000..d63ead08b --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_response.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 unbound_response unbound_response + * @{ @ingroup unbound_p + */ + +#ifndef UNBOUND_RESPONSE_H_ +#define UNBOUND_RESPONSE_H_ + +#include <resolver/resolver_response.h> +#include <unbound.h> + +typedef struct unbound_response_t unbound_response_t; + +/** + * Implementation of the resolver_response interface using libunbound. + * + */ +struct unbound_response_t { + + /** + * Implements the resolver_response interface + */ + resolver_response_t interface; +}; + +/** + * Create an unbound_response instance from a response of the unbound library. + * + * @param a response of the unbound library + * @return an unbound_response conforming to the resolver_response + * interface, or NULL on failure + */ +unbound_response_t *unbound_response_create_frm_libub_response( + struct ub_result *libub_response); + +#endif /** UNBOUND_RESPONSE_H_ @}*/ diff --git a/src/libstrongswan/plugins/unbound/unbound_rr.c b/src/libstrongswan/plugins/unbound/unbound_rr.c new file mode 100644 index 000000000..97c3b1933 --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_rr.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 <resolver/rr.h> + +#include <library.h> +#include <utils/debug.h> + +#include <stdlib.h> +#include <string.h> + +#include "unbound_rr.h" + +typedef struct private_unbound_rr_t private_unbound_rr_t; + +/** + * private data of an unbound_rr_t object. + */ +struct private_unbound_rr_t { + + /** + * Public data + */ + unbound_rr_t public; + + /** + * Owner name + */ + char* name; + + /** + * Type + */ + rr_type_t type; + + /** + * Class + */ + rr_class_t class; + + /** + * TTL + */ + uint32_t ttl; + + /** + * Size of the rdata field in octets + */ + uint16_t size; + + /** + * RDATA field (array of bytes in network order) + */ + u_char *rdata; +}; + +METHOD(rr_t, get_name, char *, + private_unbound_rr_t *this) +{ + return this->name; +} + +METHOD(rr_t, get_type, rr_type_t, + private_unbound_rr_t *this) +{ + return this->type; +} + +METHOD(rr_t, get_class, rr_class_t, + private_unbound_rr_t *this) +{ + return this->class; +} + +METHOD(rr_t, get_ttl, uint32_t, + private_unbound_rr_t *this) +{ + return this->ttl; +} + +METHOD(rr_t, get_rdata, chunk_t, + private_unbound_rr_t *this) +{ + return chunk_create(this->rdata, this->size); +} + +METHOD(rr_t, destroy, void, + private_unbound_rr_t *this) +{ + free(this->name); + free(this->rdata); + free(this); +} + +/* + * Described in header. + */ +unbound_rr_t *unbound_rr_create_frm_ldns_rr(ldns_rr *rr) +{ + private_unbound_rr_t *this; + ldns_status status; + ldns_buffer *buf; + int i; + + INIT(this, + .public = { + .interface = { + .get_name = _get_name, + .get_type = _get_type, + .get_class = _get_class, + .get_ttl = _get_ttl, + .get_rdata = _get_rdata, + .destroy = _destroy, + }, + }, + ); + + this->name = ldns_rdf2str(ldns_rr_owner(rr)); + if (!this->name) + { + DBG1(DBG_LIB, "failed to parse the owner name of a DNS RR"); + _destroy(this); + return NULL; + } + + this->type = ldns_rr_get_type(rr); + this->class = ldns_rr_get_class(rr); + this->ttl = ldns_rr_ttl(rr); + for(i = 0; i < ldns_rr_rd_count(rr); i++) + { + this->size += ldns_rdf_size(ldns_rr_rdf(rr, i)); + } + + /** + * The ldns library splits the RDATA field of a RR in various rdf. + * Here we reassemble these rdf to get the RDATA field of the RR. + */ + buf = ldns_buffer_new(LDNS_MIN_BUFLEN); + /* The buffer will be resized automatically by ldns_rr_rdata2buffer_wire() */ + status = ldns_rr_rdata2buffer_wire(buf, rr); + + if (status != LDNS_STATUS_OK) + { + DBG1(DBG_LIB, "failed to get the RDATA field of a DNS RR"); + _destroy(this); + return NULL; + } + + this->rdata = ldns_buffer_export(buf); + + return &this->public; +} diff --git a/src/libstrongswan/plugins/unbound/unbound_rr.h b/src/libstrongswan/plugins/unbound/unbound_rr.h new file mode 100644 index 000000000..d7c114f86 --- /dev/null +++ b/src/libstrongswan/plugins/unbound/unbound_rr.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 unbound_rr unbound_rr + * @{ @ingroup unbound_p + */ + +#ifndef UNBOUND_RR_H_ +#define UNBOUND_RR_H_ + +#include <resolver/rr.h> +#include <ldns/ldns.h> + +typedef struct unbound_rr_t unbound_rr_t; + +/** + * Implementation of the Resource Record interface using libunbound and libldns. + */ +struct unbound_rr_t { + + /** + * Implements the Resource Record interface + */ + rr_t interface; +}; + +/** + * Create an unbound_rr instance from a Resource Record given by + * a ldns_struct_rr from the ldns library. + * + * @return Resource Record, NULL on error + */ +unbound_rr_t *unbound_rr_create_frm_ldns_rr(ldns_rr *rr); + +#endif /** UNBOUND_RR_H_ @}*/ diff --git a/src/libstrongswan/resolver/resolver.h b/src/libstrongswan/resolver/resolver.h new file mode 100644 index 000000000..5be52b8b1 --- /dev/null +++ b/src/libstrongswan/resolver/resolver.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 resolveri resolver + * @{ @ingroup resolver + */ + +#ifndef RESOLVER_H_ +#define RESOLVER_H_ + +typedef struct resolver_t resolver_t; + +/** + * Constructor function which creates DNS resolver instances. + */ +typedef resolver_t* (*resolver_constructor_t)(void); + +#include <resolver/resolver_response.h> +#include <resolver/rr_set.h> +#include <resolver/rr.h> + +/** + * Interface of a security-aware DNS resolver. + * + */ +struct resolver_t { + + /** + * Perform a DNS query. + * + * @param domain domain (FQDN) to query + * @param rr_class class of the desired RRs + * @param rr_type type of the desired RRs + * @return response to the query, NULL on failure + */ + resolver_response_t *(*query)(resolver_t *this, char *domain, + rr_class_t rr_class, rr_type_t rr_type); + + /** + * Destroy the resolver instance. + */ + void (*destroy)(resolver_t *this); +}; + +#endif /** RESOLVER_H_ @}*/ diff --git a/src/libstrongswan/resolver/resolver_manager.c b/src/libstrongswan/resolver/resolver_manager.c new file mode 100644 index 000000000..8effe469a --- /dev/null +++ b/src/libstrongswan/resolver/resolver_manager.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 "resolver_manager.h" + +#include <utils/debug.h> + +typedef struct private_resolver_manager_t private_resolver_manager_t; + +/** + * private data of resolver_manager + */ +struct private_resolver_manager_t { + + /** + * public functions + */ + resolver_manager_t public; + + /** + * constructor function to create resolver instances + */ + resolver_constructor_t constructor; +}; + +METHOD(resolver_manager_t, add_resolver, void, + private_resolver_manager_t *this, resolver_constructor_t constructor) +{ + if (!this->constructor) + { + this->constructor = constructor; + } +} + +METHOD(resolver_manager_t, remove_resolver, void, + private_resolver_manager_t *this, resolver_constructor_t constructor) +{ + if (this->constructor == constructor) + { + this->constructor = NULL; + } +} + +METHOD(resolver_manager_t, create, resolver_t*, + private_resolver_manager_t *this) +{ + return this->constructor(); +} + +METHOD(resolver_manager_t, destroy, void, + private_resolver_manager_t *this) +{ + free(this); +} + +/* + * See header + */ +resolver_manager_t *resolver_manager_create() +{ + private_resolver_manager_t *this; + + INIT(this, + .public = { + .add_resolver = _add_resolver, + .remove_resolver = _remove_resolver, + .create = _create, + .destroy = _destroy, + }, + ); + + return &this->public; +} + diff --git a/src/libstrongswan/resolver/resolver_manager.h b/src/libstrongswan/resolver/resolver_manager.h new file mode 100644 index 000000000..6ea22aa24 --- /dev/null +++ b/src/libstrongswan/resolver/resolver_manager.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011-2012 Reto Guadagnini + * 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 resolver_manager resolver_manager +* @{ @ingroup resolver +*/ + +#ifndef RESOLVER_MANAGER_H_ +#define RESOLVER_MANAGER_H_ + +typedef struct resolver_manager_t resolver_manager_t; + +#include <resolver/resolver.h> + +/** + * The resolver_manager manages the resolver implementations and + * creates instances of them. + * + * A resolver plugin is registered by providing its constructor function + * to the manager. The manager creates instances of the resolver plugin + * using the registered constructor function. + */ +struct resolver_manager_t { + + /** + * Register a resolver implementation. + * + * @param constructor resolver constructor function + */ + void (*add_resolver)(resolver_manager_t *this, + resolver_constructor_t constructor); + + /** + * Unregister a previously registered resolver implementation. + * + * @param constructor resolver constructor function to unregister + */ + void (*remove_resolver)(resolver_manager_t *this, + resolver_constructor_t constructor); + + /** + * Get a new resolver instance. + * + * @return resolver instance. + */ + resolver_t* (*create)(resolver_manager_t *this); + + /** + * Destroy a resolver_manager instance. + */ + void (*destroy)(resolver_manager_t *this); +}; + +/** + * Create a resolver_manager instance. + */ +resolver_manager_t *resolver_manager_create(); + +#endif /** RESOLVER_MANAGER_H_ @}*/ diff --git a/src/libstrongswan/resolver/resolver_response.h b/src/libstrongswan/resolver/resolver_response.h new file mode 100644 index 000000000..e45fb6401 --- /dev/null +++ b/src/libstrongswan/resolver/resolver_response.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 rsolver_response resolver_response + * @{ @ingroup resolver + */ + +#ifndef RESOLVER_RESPONSE_H_ +#define RESOLVER_RESPONSE_H_ + +typedef struct resolver_response_t resolver_response_t; +typedef enum dnssec_status_t dnssec_status_t; + +#include <library.h> +#include <resolver/rr_set.h> + +/** + * DNSSEC security state. + * + * DNSSEC security state, which a security aware resolver is able determine + * according to RFC 4033. + */ +enum dnssec_status_t { + /** + * The validating resolver has a trust anchor, has a chain of + * trust, and is able to verify all the signatures in the response. + * [RFC4033] + */ + SECURE, + /** + * The validating resolver has a trust anchor, a chain of + * trust, and, at some delegation point, signed proof of the + * non-existence of a DS record. This indicates that subsequent + * branches in the tree are provably insecure. A validating resolver + * may have a local policy to mark parts of the domain space as + * insecure. [RFC4033] + */ + INSECURE, + /** + * The validating resolver has a trust anchor and a secure + * delegation indicating that subsidiary data is signed, but the + * response fails to validate for some reason: missing signatures, + * expired signatures, signatures with unsupported algorithms, data + * missing that the relevant NSEC RR says should be present, and so + * forth. [RFC4033] + */ + BOGUS, + /** + * There is no trust anchor that would indicate that a + * specific portion of the tree is secure. This is the default + * operation mode. [RFC4033] + */ + INDETERMINATE, +}; + + +/** + * A response of the DNS resolver to a DNS query. + * + * A response represents the answer of the Domain Name System to a query. + * It contains the RRset with the queried Resource Records and additional + * information. + */ +struct resolver_response_t { + + /** + * Get the original question string. + * + * The string to which the returned pointer points, is still owned + * by the resolver_response. Clone it if necessary. + * + * @return the queried name + */ + char *(*get_query_name)(resolver_response_t *this); + + /** + * Get the canonical name of the result. + * + * The string to which the returned pointer points, is still owned + * by the resolver_response. Clone it if necessary. + * + * @return - canonical name of result + * - NULL, if result has no canonical name + */ + char *(*get_canon_name)(resolver_response_t *this); + + /** + * Does the RRset of this response contain some Resource Records? + * + * Returns TRUE if the RRset of this response contains some RRs + * (RRSIG Resource Records are ignored). + * + * @return + * - TRUE, if there are some RRs in the RRset + * - FALSE, otherwise + */ + bool (*has_data)(resolver_response_t *this); + + /** + * Does the queried name exist? + * + * @return + * - TRUE, if the queried name exists + * - FALSE, otherwise + */ + bool (*query_name_exist)(resolver_response_t *this); + + /** + * Get the DNSSEC security state of the response. + * + * @return DNSSEC security state + */ + dnssec_status_t (*get_security_state)(resolver_response_t *this); + + /** + * Get the RRset with all Resource Records of this response. + * + * @return - RRset + * - NULL if there is no data or the query name + * does not exist + */ + rr_set_t *(*get_rr_set)(resolver_response_t *this); + + /** + * Destroy this response. + */ + void (*destroy) (resolver_response_t *this); +}; + +#endif /** RR_SET_H_ @}*/ diff --git a/src/libstrongswan/resolver/rr.h b/src/libstrongswan/resolver/rr.h new file mode 100644 index 000000000..109ec5135 --- /dev/null +++ b/src/libstrongswan/resolver/rr.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 rr rr + * @{ @ingroup resolver + */ + +#ifndef RR_H_ +#define RR_H_ + +typedef struct rr_t rr_t; +typedef enum rr_type_t rr_type_t; +typedef enum rr_class_t rr_class_t; + +#include <library.h> + +/** + * Resource Record types. + * + * According to www.iana.org/assignments/dns-parameters (version 2012-03-13). + */ +enum rr_type_t { + /** a host address */ + RR_TYPE_A = 1, + /** an authoritative name server */ + RR_TYPE_NS = 2, + //** a mail destination (OBSOLETE - use MX */ + RR_TYPE_MD = 3, + /** a mail forwarder (OBSOLETE - use MX) */ + RR_TYPE_MF = 4, + /** the canonical name for an alias */ + RR_TYPE_CNAME = 5, + /** marks the start of a zone of authority */ + RR_TYPE_SOA = 6, + /** a mailbox domain name (EXPERIMENTAL) */ + RR_TYPE_MB = 7, + /** a mail group member (EXPERIMENTAL) */ + RR_TYPE_MG = 8, + /** a mail rename domain name (EXPERIMENTAL) */ + RR_TYPE_MR = 9, + /** a null RR (EXPERIMENTAL) */ + RR_TYPE_NULL = 10, + /** a well known service description */ + RR_TYPE_WKS = 11, + /** a domain name pointer */ + RR_TYPE_PTR = 12, + /** host information */ + RR_TYPE_HINFO = 13, + /** mailbox or mail list information */ + RR_TYPE_MINFO = 14, + /** mail exchange */ + RR_TYPE_MX = 15, + /** text strings */ + RR_TYPE_TXT = 16, + /** for Responsible Person */ + RR_TYPE_RP = 17, + /** for AFS Data Base location */ + RR_TYPE_AFSDB = 18, + /** for X.25 PSDN address */ + RR_TYPE_X25 = 19, + /** for ISDN address */ + RR_TYPE_ISDN = 20, + /** for Route Through */ + RR_TYPE_RT = 21, + /** for NSAP address, NSAP style A record */ + RR_TYPE_NSAP = 22, + /** for domain name pointer, NSAP style */ + RR_TYPE_NSAP_PTR = 23, + /** for security signature */ + RR_TYPE_SIG = 24, + /** for security key */ + RR_TYPE_KEY = 25, + /** X.400 mail mapping information */ + RR_TYPE_PX = 26, + /** Geographical Position */ + RR_TYPE_GPOS = 27, + /** ipv6 address */ + RR_TYPE_AAAA = 28, + /** Location Information */ + RR_TYPE_LOC = 29, + /** Next Domain (OBSOLETE) */ + RR_TYPE_NXT = 30, + /** Endpoint Identifier */ + RR_TYPE_EID = 31, + /** Nimrod Locator */ + RR_TYPE_NIMLOC = 32, + /** Server Selection */ + RR_TYPE_SRV = 33, + /** ATM Address */ + RR_TYPE_ATMA = 34, + /** Naming Authority Pointer */ + RR_TYPE_NAPTR = 35, + /** Key Exchanger */ + RR_TYPE_KX = 36, + /** CERT */ + RR_TYPE_CERT = 37, + /** A6 (OBSOLETE - use AAAA) */ + RR_TYPE_A6 = 38, + /** DNAME */ + RR_TYPE_DNAME = 39, + /** SINK */ + RR_TYPE_SINK = 40, + /** OPT */ + RR_TYPE_OPT = 41, + /** APL */ + RR_TYPE_APL = 42, + /** Delegation Signer */ + RR_TYPE_DS = 43, + /** SSH Key Fingerprint */ + RR_TYPE_SSHFP = 44, + /** IPSECKEY */ + RR_TYPE_IPSECKEY = 45, + /** RRSIG */ + RR_TYPE_RRSIG = 46, + /** NSEC */ + RR_TYPE_NSEC = 47, + /** DNSKEY */ + RR_TYPE_DNSKEY = 48, + /** DHCID */ + RR_TYPE_DHCID = 49, + /** NSEC3 */ + RR_TYPE_NSEC3 = 50, + /** NSEC3PARAM */ + RR_TYPE_NSEC3PARAM = 51, + + /** Unassigned 52-54 */ + + /** Host Identity Protocol */ + RR_TYPE_HIP = 55, + /** NINFO */ + RR_TYPE_NINFO = 56, + /** RKEY */ + RR_TYPE_RKEY = 57, + /** Trust Anchor LINK */ + RR_TYPE_TALINK = 58, + /** Child DS */ + RR_TYPE_CDS = 59, + + /** Unassigned 60-98 */ + + /** SPF */ + RR_TYPE_SPF = 99, + /** UINFO */ + RR_TYPE_UINFO = 100, + /** UID */ + RR_TYPE_UID = 101, + /** GID */ + RR_TYPE_GID = 102, + /** UNSPEC */ + RR_TYPE_UNSPEC = 103, + + /** Unassigned 104-248 */ + + /** Transaction Key */ + RR_TYPE_TKEY = 249, + /** Transaction Signature */ + RR_TYPE_TSIG = 250, + /** incremental transfer */ + RR_TYPE_IXFR = 251, + /** transfer of an entire zone */ + RR_TYPE_AXFR = 252, + /** mailbox-related RRs (MB, MG or MR) */ + RR_TYPE_MAILB = 253, + /** mail agent RRs (OBSOLETE - see MX) */ + RR_TYPE_MAILA = 254, + /** A request for all records */ + RR_TYPE_ANY = 255, + /** URI */ + RR_TYPE_URI = 256, + /** Certification Authority Authorization */ + RR_TYPE_CAA = 257, + + /** Unassigned 258-32767 */ + + /** DNSSEC Trust Authorities */ + RR_TYPE_TA = 32768, + /** DNSSEC Lookaside Validation */ + RR_TYPE_DLV = 32769, + + /** Unassigned 32770-65279 */ + + /** Private use 65280-65534 */ + + /** Reserved 65535 */ +}; + + +/** + * Resource Record CLASSes + */ +enum rr_class_t { + /** Internet */ + RR_CLASS_IN = 1, + /** Chaos */ + RR_CLASS_CH = 3, + /** Hesiod */ + RR_CLASS_HS = 4, + /** further CLASSes: http://wwwiana.org/assignments/dns-parameters */ +}; + + +/** + * A DNS Resource Record. + * + * Represents a Resource Record of the Domain Name System + * as defined in RFC 1035. + * + */ +struct rr_t { + + /** + * Get the NAME of the owner of this RR. + * + * @return owner name as string + */ + char *(*get_name)(rr_t *this); + + /** + * Get the type of this RR. + * + * @return RR type + */ + rr_type_t (*get_type)(rr_t *this); + + /** + * Get the class of this RR. + * + * @return RR class + */ + rr_class_t (*get_class)(rr_t *this); + + /** + * Get the Time to Live (TTL) of this RR. + * + * @return Time to Live + */ + uint32_t (*get_ttl)(rr_t *this); + + /** + * Get the content of the RDATA field as chunk. + * + * The data pointed by the chunk is still owned by the RR. + * Clone it if needed. + * + * @return RDATA field as chunk + */ + chunk_t (*get_rdata)(rr_t *this); + + /** + * Destroy the Resource Record. + */ + void (*destroy) (rr_t *this); +}; + +#endif /** RR_H_ @}*/ diff --git a/src/libstrongswan/resolver/rr_set.c b/src/libstrongswan/resolver/rr_set.c new file mode 100644 index 000000000..dea5c4086 --- /dev/null +++ b/src/libstrongswan/resolver/rr_set.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 "rr_set.h" + +#include <library.h> +#include <utils/debug.h> + +typedef struct private_rr_set_t private_rr_set_t; + +/** +* private data of the rr_set +*/ +struct private_rr_set_t { + + /** + * public functions + */ + rr_set_t public; + + /** + * List of Resource Records which form the RRset + */ + linked_list_t *rr_list; + + /** + * List of the signatures (RRSIGs) of the Resource Records contained in + * this set + */ + linked_list_t *rrsig_list; +}; + +METHOD(rr_set_t, create_rr_enumerator, enumerator_t*, + private_rr_set_t *this) +{ + return this->rr_list->create_enumerator(this->rr_list); +} + +METHOD(rr_set_t, create_rrsig_enumerator, enumerator_t*, + private_rr_set_t *this) +{ + if (this->rrsig_list) + { + return this->rrsig_list->create_enumerator(this->rrsig_list); + } + return NULL; +} + +METHOD(rr_set_t, destroy, void, + private_rr_set_t *this) +{ + this->rr_list->destroy_offset(this->rr_list, + offsetof(rr_t, destroy)); + if (this->rrsig_list) + { + this->rrsig_list->destroy_offset(this->rrsig_list, + offsetof(rr_t, destroy)); + } + free(this); +} + +/* + * see header + */ +rr_set_t *rr_set_create(linked_list_t *list_of_rr, linked_list_t *list_of_rrsig) +{ + private_rr_set_t *this; + + INIT(this, + .public = { + .create_rr_enumerator = _create_rr_enumerator, + .create_rrsig_enumerator = _create_rrsig_enumerator, + .destroy = _destroy, + }, + ); + + if (list_of_rr == NULL) + { + DBG1(DBG_LIB, "could not create a rr_set without a list_of_rr"); + _destroy(this); + return NULL; + } + this->rr_list = list_of_rr; + this->rrsig_list = list_of_rrsig; + + return &this->public; +} + diff --git a/src/libstrongswan/resolver/rr_set.h b/src/libstrongswan/resolver/rr_set.h new file mode 100644 index 000000000..5a1737a05 --- /dev/null +++ b/src/libstrongswan/resolver/rr_set.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2012 Reto Guadagnini + * 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 rr_set rr_set + * @{ @ingroup resolver + */ + +#ifndef RR_SET_H_ +#define RR_SET_H_ + +typedef struct rr_set_t rr_set_t; + +#include <library.h> +#include <collections/enumerator.h> +#include <collections/linked_list.h> + +/** + * A set of DNS Resource Records. + * + * Represents a RRset as defined in RFC 2181. This RRset consists of a set of + * Resource Records with the same label, class and type but different data. + * + * The DNSSEC signature Resource Records (RRSIGs) which sign the RRs of this set + * are also part of an object of this type. + */ +struct rr_set_t { + + /** + * Create an enumerator over all Resource Records of this RRset. + * + * @note The enumerator's position is invalid before the first call + * to enumerate(). + * + * @return enumerator over Resource Records + */ + enumerator_t *(*create_rr_enumerator)(rr_set_t *this); + + /** + * Create an enumerator over all RRSIGs of this RRset + * + * @note The enumerator's position is invalid before the first call + * to enumerate(). + * + * @return enumerator over RRSIG Resource Records, + * NULL if there are no RRSIGs for this RRset + */ + enumerator_t *(*create_rrsig_enumerator)(rr_set_t *this); + + /** + * Destroy this RRset with all its Resource Records. + */ + void (*destroy) (rr_set_t *this); +}; + +/** + * Create an rr_set instance. + * + * @param list_of_rr list of Resource Records which form this RRset + * @param list_of_rrsig list of the signatures (RRSIGs) of the + * Resource Records of this set + * @return Resource Record set, NULL on failure + */ +rr_set_t *rr_set_create(linked_list_t *list_of_rr, + linked_list_t *list_of_rrsig); + +#endif /** RR_SET_H_ @}*/ diff --git a/src/libstrongswan/threading/thread.c b/src/libstrongswan/threading/thread.c index e524409c7..d6d98d1ef 100644 --- a/src/libstrongswan/threading/thread.c +++ b/src/libstrongswan/threading/thread.c @@ -129,7 +129,11 @@ static thread_value_t *current_thread; #ifndef HAVE_PTHREAD_CANCEL /* if pthread_cancel is not available, we emulate it using a signal */ +#ifdef ANDROID +#define SIG_CANCEL SIGUSR2 +#else #define SIG_CANCEL (SIGRTMIN+7) +#endif /* the signal handler for SIG_CANCEL uses pthread_exit to terminate the * "cancelled" thread */ diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c index c36a76efe..44a14496c 100644 --- a/src/libstrongswan/utils/capabilities.c +++ b/src/libstrongswan/utils/capabilities.c @@ -29,7 +29,9 @@ #include <utils/debug.h> -#if !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETGRNAM_R) +#if !defined(HAVE_GETPWNAM_R) || \ + !defined(HAVE_GETGRNAM_R) || \ + !defined(HAVE_GETPWUID_R) # include <threading/mutex.h> # define EMULATE_R_FUNCS #endif @@ -188,6 +190,34 @@ METHOD(capabilities_t, resolve_gid, bool, return FALSE; } +/** + * Initialize supplementary groups for unprivileged user + */ +static bool init_supplementary_groups(private_capabilities_t *this) +{ + struct passwd *pwp; + int res = -1; + +#ifdef HAVE_GETPWUID_R + struct passwd pwd; + char buf[1024]; + + if (getpwuid_r(this->uid, &pwd, buf, sizeof(buf), &pwp) == 0 && pwp) + { + res = initgroups(pwp->pw_name, this->gid); + } +#else /* HAVE_GETPWUID_R */ + this->mutex->lock(this->mutex); + pwp = getpwuid(this->uid); + if (pwp) + { + res = initgroups(pwp->pw_name, this->gid); + } + this->mutex->unlock(this->mutex); +#endif /* HAVE_GETPWUID_R */ + return res == 0; +} + METHOD(capabilities_t, drop, bool, private_capabilities_t *this) { @@ -195,6 +225,12 @@ METHOD(capabilities_t, drop, bool, prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); #endif + if (!init_supplementary_groups(this)) + { + DBG1(DBG_LIB, "initializing supplementary groups for %u failed", + this->uid); + return FALSE; + } if (this->gid && setgid(this->gid) != 0) { DBG1(DBG_LIB, "change to unprivileged group %u failed: %s", diff --git a/src/libstrongswan/utils/chunk.h b/src/libstrongswan/utils/chunk.h index 67848eec1..bc14b7394 100644 --- a/src/libstrongswan/utils/chunk.h +++ b/src/libstrongswan/utils/chunk.h @@ -191,6 +191,11 @@ static inline void chunk_clear(chunk_t *chunk) #define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing)) /** + * Initialize a chunk from a static string, not containing 0-terminator + */ +#define chunk_from_str(str) chunk_create(str, strlen(str)) + +/** * Allocate a chunk on the heap */ #define chunk_alloc(bytes) ({size_t x = (bytes); chunk_create(x ? malloc(x) : NULL, x);}) diff --git a/src/libstrongswan/utils/utils.c b/src/libstrongswan/utils/utils.c index bf0224c5f..0850df9bc 100644 --- a/src/libstrongswan/utils/utils.c +++ b/src/libstrongswan/utils/utils.c @@ -387,6 +387,14 @@ status_t return_failed() } /** + * returns SUCCESS + */ +status_t return_success() +{ + return SUCCESS; +} + +/** * nop operation */ void nop() @@ -460,7 +468,7 @@ int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, bool utc = *((bool*)(args[1]));; struct tm t; - if (time == UNDEFINED_TIME) + if (*time == UNDEFINED_TIME) { return print_in_hook(data, "--- -- --:--:--%s----", utc ? " UTC " : " "); diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h index 7b1beb93a..6c24a261f 100644 --- a/src/libstrongswan/utils/utils.h +++ b/src/libstrongswan/utils/utils.h @@ -496,6 +496,11 @@ bool return_false(); status_t return_failed(); /** + * returns SUCCESS + */ +status_t return_success(); + +/** * Write a 16-bit host order value in network order to an unaligned address. * * @param host host order 16-bit value diff --git a/src/libtls/tls.c b/src/libtls/tls.c index dea08e3eb..85a05a00b 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -359,6 +359,18 @@ METHOD(tls_t, is_server, bool, return this->is_server; } +METHOD(tls_t, get_server_id, identification_t*, + private_tls_t *this) +{ + return this->server; +} + +METHOD(tls_t, get_peer_id, identification_t*, + private_tls_t *this) +{ + return this->peer; +} + METHOD(tls_t, get_version, tls_version_t, private_tls_t *this) { @@ -457,6 +469,8 @@ tls_t *tls_create(bool is_server, identification_t *server, .process = _process, .build = _build, .is_server = _is_server, + .get_server_id = _get_server_id, + .get_peer_id = _get_peer_id, .get_version = _get_version, .set_version = _set_version, .get_purpose = _get_purpose, diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 6b4876f73..c8186b829 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -193,6 +193,20 @@ struct tls_t { bool (*is_server)(tls_t *this); /** + * Return the server identity + * + * @return Server identity + */ + identification_t* (*get_server_id)(tls_t *this); + + /** + * Return the peer identity + * + * @return Peer identity + */ + identification_t* (*get_peer_id)(tls_t *this); + + /** * Get the negotiated TLS/SSL version. * * @return negotiated TLS version diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index c76376b43..6e4347e3c 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -223,7 +223,7 @@ static status_t process_application(private_tls_fragmentation_t *this, continue; case SUCCESS: this->application_finished = TRUE; - return SUCCESS; + /* FALL */ case FAILED: default: this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); @@ -368,7 +368,7 @@ static status_t build_application(private_tls_fragmentation_t *this) break; case SUCCESS: this->application_finished = TRUE; - break; + /* FALL */ case FAILED: default: this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); @@ -391,6 +391,10 @@ METHOD(tls_fragmentation_t, build, status_t, this->state = ALERT_SENT; return INVALID_STATE; case ALERT_SENT: + if (this->application_finished) + { + return SUCCESS; + } return FAILED; case ALERT_NONE: break; diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index 75b714e30..52e2cd629 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -42,14 +42,39 @@ struct private_tls_application_t { tls_application_t application; /** - * Chunk of data to send + * Output buffer to write to */ chunk_t out; /** - * Chunk of data received + * Number of bytes written to out + */ + size_t out_done; + + /** + * Input buffer to read to */ chunk_t in; + + /** + * Number of bytes read to in + */ + size_t in_done; + + /** + * Cached input data + */ + chunk_t cache; + + /** + * Bytes cosnumed in cache + */ + size_t cache_done; + + /** + * Close TLS connection? + */ + bool close; }; /** @@ -82,22 +107,44 @@ METHOD(tls_application_t, process, status_t, private_tls_application_t *this, bio_reader_t *reader) { chunk_t data; + size_t len; - if (!reader->read_data(reader, reader->remaining(reader), &data)) + if (this->close) { - return FAILED; + return SUCCESS; + } + len = min(reader->remaining(reader), this->in.len - this->in_done); + if (len) + { /* copy to read buffer as much as fits in */ + if (!reader->read_data(reader, len, &data)) + { + return FAILED; + } + memcpy(this->in.ptr + this->in_done, data.ptr, data.len); + this->in_done += data.len; + } + else + { /* read buffer is full, cache for next read */ + if (!reader->read_data(reader, reader->remaining(reader), &data)) + { + return FAILED; + } + this->cache = chunk_cat("mc", this->cache, data); } - this->in = chunk_cat("mc", this->in, data); return NEED_MORE; } METHOD(tls_application_t, build, status_t, private_tls_application_t *this, bio_writer_t *writer) { - if (this->out.len) + if (this->close) + { + return SUCCESS; + } + if (this->out.len > this->out_done) { writer->write_data(writer, this->out); - this->out = chunk_empty; + this->out_done = this->out.len; return NEED_MORE; } return INVALID_STATE; @@ -106,11 +153,11 @@ METHOD(tls_application_t, build, status_t, /** * TLS data exchange loop */ -static bool exchange(private_tls_socket_t *this, bool wr) +static bool exchange(private_tls_socket_t *this, bool wr, bool block) { char buf[CRYPTO_BUF_SIZE], *pos; ssize_t len, out; - int round = 0; + int round = 0, flags; for (round = 0; TRUE; round++) { @@ -137,6 +184,8 @@ static bool exchange(private_tls_socket_t *this, bool wr) continue; case INVALID_STATE: break; + case SUCCESS: + return TRUE; default: return FALSE; } @@ -144,55 +193,97 @@ static bool exchange(private_tls_socket_t *this, bool wr) } if (wr) { - if (this->app.out.len == 0) + if (this->app.out_done == this->app.out.len) { /* all data written */ return TRUE; } } else { - if (this->app.in.len) - { /* some data received */ + if (this->app.in_done == this->app.in.len) + { /* buffer fully received */ return TRUE; } - if (round > 0) - { /* did some handshaking, return empty chunk to not block */ - return TRUE; + } + + flags = 0; + if (this->app.out_done == this->app.out.len) + { + if (!block || this->app.in_done) + { + flags |= MSG_DONTWAIT; } } - len = read(this->fd, buf, sizeof(buf)); - if (len <= 0) + len = recv(this->fd, buf, sizeof(buf), flags); + if (len < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + if (this->app.in_done == 0) + { + /* reading, nothing got yet, and call would block */ + errno = EWOULDBLOCK; + this->app.in_done = -1; + } + return TRUE; + } return FALSE; } - if (this->tls->process(this->tls, buf, len) != NEED_MORE) + if (len == 0) + { /* EOF */ + return TRUE; + } + switch (this->tls->process(this->tls, buf, len)) { - return FALSE; + case NEED_MORE: + break; + case SUCCESS: + return TRUE; + default: + return FALSE; } } } -METHOD(tls_socket_t, read_, bool, - private_tls_socket_t *this, chunk_t *buf) +METHOD(tls_socket_t, read_, ssize_t, + private_tls_socket_t *this, void *buf, size_t len, bool block) { - if (exchange(this, FALSE)) + if (this->app.cache.len) + { + size_t cache; + + cache = min(len, this->app.cache.len - this->app.cache_done); + memcpy(buf, this->app.cache.ptr + this->app.cache_done, cache); + + this->app.cache_done += cache; + if (this->app.cache_done == this->app.cache.len) + { + chunk_free(&this->app.cache); + this->app.cache_done = 0; + } + return cache; + } + this->app.in.ptr = buf; + this->app.in.len = len; + this->app.in_done = 0; + if (exchange(this, FALSE, block)) { - *buf = this->app.in; - this->app.in = chunk_empty; - return TRUE; + return this->app.in_done; } - return FALSE; + return -1; } -METHOD(tls_socket_t, write_, bool, - private_tls_socket_t *this, chunk_t buf) +METHOD(tls_socket_t, write_, ssize_t, + private_tls_socket_t *this, void *buf, size_t len) { - this->app.out = buf; - if (exchange(this, TRUE)) + this->app.out.ptr = buf; + this->app.out.len = len; + this->app.out_done = 0; + if (exchange(this, TRUE, FALSE)) { - return TRUE; + return this->app.out_done; } - return FALSE; + return -1; } METHOD(tls_socket_t, splice, bool, @@ -200,68 +291,85 @@ METHOD(tls_socket_t, splice, bool, { char buf[PLAIN_BUF_SIZE], *pos; fd_set set; - chunk_t data; - ssize_t len; - bool old; + ssize_t in, out; + bool old, plain_eof = FALSE, crypto_eof = FALSE; - while (TRUE) + while (!plain_eof && !crypto_eof) { FD_ZERO(&set); FD_SET(rfd, &set); FD_SET(this->fd, &set); old = thread_cancelability(TRUE); - len = select(max(rfd, this->fd) + 1, &set, NULL, NULL, NULL); + in = select(max(rfd, this->fd) + 1, &set, NULL, NULL, NULL); thread_cancelability(old); - if (len == -1) + if (in == -1) { DBG1(DBG_TLS, "TLS select error: %s", strerror(errno)); return FALSE; } - if (FD_ISSET(this->fd, &set)) + while (!plain_eof && FD_ISSET(this->fd, &set)) { - if (!read_(this, &data)) - { - DBG2(DBG_TLS, "TLS read error/disconnect"); - return TRUE; - } - pos = data.ptr; - while (data.len) + in = read_(this, buf, sizeof(buf), FALSE); + switch (in) { - len = write(wfd, pos, data.len); - if (len == -1) - { - free(data.ptr); - DBG1(DBG_TLS, "TLS plain write error: %s", strerror(errno)); - return FALSE; - } - data.len -= len; - pos += len; + case 0: + plain_eof = TRUE; + break; + case -1: + if (errno != EWOULDBLOCK) + { + DBG1(DBG_TLS, "TLS read error: %s", strerror(errno)); + return FALSE; + } + break; + default: + pos = buf; + while (in) + { + out = write(wfd, pos, in); + if (out == -1) + { + DBG1(DBG_TLS, "TLS plain write error: %s", + strerror(errno)); + return FALSE; + } + in -= out; + pos += out; + } + continue; } - free(data.ptr); + break; } - if (FD_ISSET(rfd, &set)) + if (!crypto_eof && FD_ISSET(rfd, &set)) { - len = read(rfd, buf, sizeof(buf)); - if (len > 0) + in = read(rfd, buf, sizeof(buf)); + switch (in) { - if (!write_(this, chunk_create(buf, len))) - { - DBG1(DBG_TLS, "TLS write error"); - return FALSE; - } - } - else - { - if (len < 0) - { + case 0: + crypto_eof = TRUE; + break; + case -1: DBG1(DBG_TLS, "TLS plain read error: %s", strerror(errno)); return FALSE; - } - return TRUE; + default: + pos = buf; + while (in) + { + out = write_(this, pos, in); + if (out == -1) + { + DBG1(DBG_TLS, "TLS write error"); + return FALSE; + } + in -= out; + pos += out; + } + break; } } } + return TRUE; } METHOD(tls_socket_t, get_fd, int, @@ -273,8 +381,11 @@ METHOD(tls_socket_t, get_fd, int, METHOD(tls_socket_t, destroy, void, private_tls_socket_t *this) { + /* send a TLS close notify if not done yet */ + this->app.close = TRUE; + write_(this, NULL, 0); + free(this->app.cache.ptr); this->tls->destroy(this->tls); - free(this->app.in.ptr); free(this); } diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index edd05fd29..4ddddc19e 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -35,24 +35,27 @@ typedef struct tls_socket_t tls_socket_t; struct tls_socket_t { /** - * Read data from secured socket, return allocated chunk. + * Read data from secured socket. * * This call is blocking, you may use select() on the underlying socket to - * wait for data. If the there was non-application data available, the - * read function can return an empty chunk. + * wait for data. If "block" is FALSE and no application data is available, + * the function returns -1 and sets errno to EWOULDBLOCK. * - * @param data pointer to allocate received data - * @return TRUE if data received successfully + * @param buf buffer to write received data to + * @param len size of buffer + * @param block TRUE to block this call, FALSE to fail if it would block + * @return number of bytes read, 0 on EOF, -1 on error */ - bool (*read)(tls_socket_t *this, chunk_t *data); + ssize_t (*read)(tls_socket_t *this, void *buf, size_t len, bool block); /** - * Write a chunk of data over the secured socket. + * Write data over the secured socket. * - * @param data data to send - * @return TRUE if data sent successfully + * @param buf data to send + * @param len number of bytes to write from buf + * @return number of bytes written, -1 on error */ - bool (*write)(tls_socket_t *this, chunk_t data); + ssize_t (*write)(tls_socket_t *this, void *buf, size_t len); /** * Read/write plain data from file descriptor. diff --git a/src/libtnccs/Makefile.am b/src/libtnccs/Makefile.am index 449d32d92..c6492d8d3 100644 --- a/src/libtnccs/Makefile.am +++ b/src/libtnccs/Makefile.am @@ -1,5 +1,8 @@ -INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libtncif +INCLUDES = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libtncif \ + -I$(top_srcdir)/src/libtls ipseclib_LTLIBRARIES = libtnccs.la diff --git a/src/libtnccs/tnc/tnccs/tnccs.h b/src/libtnccs/tnc/tnccs/tnccs.h index c3020d7c3..fd3e5cabb 100644 --- a/src/libtnccs/tnc/tnccs/tnccs.h +++ b/src/libtnccs/tnc/tnccs/tnccs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ typedef struct tnccs_t tnccs_t; typedef enum tnccs_type_t tnccs_type_t; +typedef enum tnc_ift_type_t tnc_ift_type_t; #include <tncif.h> #include <tncifimc.h> @@ -34,6 +35,8 @@ typedef enum tnccs_type_t tnccs_type_t; #include <library.h> #include <plugins/plugin.h> +#include <tls.h> + /** * Type of TNC Client/Server protocol */ @@ -46,17 +49,75 @@ enum tnccs_type_t { }; /** + * Type of TNC Transport protocol + */ +enum tnc_ift_type_t { + TNC_IFT_UNKNOWN, + TNC_IFT_EAP_1_0, + TNC_IFT_EAP_1_1, + TNC_IFT_EAP_2_0, + TNC_IFT_TLS_1_0, + TNC_IFT_TLS_2_0 +}; + +/** * enum names for tnccs_type_t. */ extern enum_name_t *tnccs_type_names; /** + * TNCCS public interface + */ +struct tnccs_t { + + /** + * Implements tls_t + */ + tls_t tls; + + /** + * Get underlying TNC IF-T transport protocol + * + * @return TNC IF-T transport protocol + */ + tnc_ift_type_t (*get_transport)(tnccs_t *this); + + /** + * Set underlying TNC IF-T transport protocol + * + * @param transport TNC IF-T transport protocol + */ + void (*set_transport)(tnccs_t *this, tnc_ift_type_t transport); + + /** + * Get type of TNC Client authentication + * + * @return TNC Client authentication type + */ + u_int32_t (*get_auth_type)(tnccs_t *this); + + /** + * Set type of TNC Client authentication + * + * @param auth_type TNC Client authentication type + */ + void (*set_auth_type)(tnccs_t *this, u_int32_t auth_type); + +}; + +/** * Constructor definition for a pluggable TNCCS protocol implementation. * * @param is_server TRUE if TNC Server, FALSE if TNC Client + * @param server Server identity + * @param peer Client identity + * @param transport Underlying TNC IF-T transport protocol used * @return implementation of the tnccs_t interface */ -typedef tnccs_t *(*tnccs_constructor_t)(bool is_server); +typedef tnccs_t *(*tnccs_constructor_t)(bool is_server, + identification_t *server, + identification_t *peer, + tnc_ift_type_t transport); /** * Callback function adding a message to a TNCCS batch diff --git a/src/libtnccs/tnc/tnccs/tnccs_manager.h b/src/libtnccs/tnc/tnccs/tnccs_manager.h index cbf2dc0e9..4ab9d7e18 100644 --- a/src/libtnccs/tnc/tnccs/tnccs_manager.h +++ b/src/libtnccs/tnc/tnccs/tnccs_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2010-2013 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -56,10 +56,15 @@ struct tnccs_manager_t { * * @param type type of the TNCCS protocol * @param is_server TRUE if TNC Server, FALSE if TNC Client + * @param server Server identity + * @param peer Client identity + * @param transport Underlying TNC IF-T transport protocol used * @return TNCCS protocol instance, NULL if no constructor found */ tnccs_t* (*create_instance)(tnccs_manager_t *this, tnccs_type_t type, - bool is_server); + bool is_server, identification_t *server, + identification_t *peer, + tnc_ift_type_t transport); /** * Create a TNCCS connection and assign a unique connection ID as well a diff --git a/src/libtncif/Android.mk b/src/libtncif/Android.mk index ef406dd59..a337a0dcc 100644 --- a/src/libtncif/Android.mk +++ b/src/libtncif/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) # copy-n-paste from Makefile.am LOCAL_SRC_FILES := \ tncif.h tncifimc.h tncifimv.h tncif_names.h tncif_names.c \ +tncif_identity.h tncif_identity.c \ tncif_pa_subtypes.h tncif_pa_subtypes.c # build libtncif --------------------------------------------------------------- diff --git a/src/libtncif/Makefile.am b/src/libtncif/Makefile.am index cc262ffca..6da1201f3 100644 --- a/src/libtncif/Makefile.am +++ b/src/libtncif/Makefile.am @@ -4,6 +4,7 @@ noinst_LTLIBRARIES = libtncif.la libtncif_la_SOURCES = \ tncif.h tncifimc.h tncifimv.h tncif_names.h tncif_names.c \ +tncif_identity.h tncif_identity.c \ tncif_pa_subtypes.h tncif_pa_subtypes.c EXTRA_DIST = Android.mk diff --git a/src/libtncif/tncif_identity.c b/src/libtncif/tncif_identity.c new file mode 100644 index 000000000..66de83dbd --- /dev/null +++ b/src/libtncif/tncif_identity.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2013 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. + */ + +#include "tncif_identity.h" + +#include <bio/bio_writer.h> +#include <bio/bio_reader.h> +#include <pen/pen.h> +#include <utils/debug.h> + +typedef struct private_tncif_identity_t private_tncif_identity_t; + +/** + * TNC Identity List Attribute Format (TCG TNC IF-IMV 1.4 Draft) + * + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Identity Count | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RESERVED | Identity Type Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Identity Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Identity Value Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * ~ Identity Value ~ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RESERVED | Subject Type Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Subject Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RESERVED | Authentication Method Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Authentication Method | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/** + * Private data of a tncif_identity_t object. + * + */ +struct private_tncif_identity_t { + + /** + * Public tncif_identity_t interface. + */ + tncif_identity_t public; + + /** + * Identity Type + */ + pen_type_t identity_type; + + /** + * Identity Value + */ + chunk_t identity_value; + + /** + * Subject Type + */ + pen_type_t subject_type; + + /** + * Authentication Type + */ + pen_type_t auth_type; +}; + +METHOD(tncif_identity_t, get_identity_type, pen_type_t, + private_tncif_identity_t *this) +{ + return this->identity_type; +} + +METHOD(tncif_identity_t, get_identity_value, chunk_t, + private_tncif_identity_t *this) +{ + return this->identity_value; +} + +METHOD(tncif_identity_t, get_subject_type, pen_type_t, + private_tncif_identity_t *this) +{ + return this->subject_type; +} + +METHOD(tncif_identity_t, get_auth_type, pen_type_t, + private_tncif_identity_t *this) +{ + return this->auth_type; +} + +METHOD(tncif_identity_t, build, void, + private_tncif_identity_t *this, bio_writer_t *writer) +{ + writer->write_uint32(writer, this->identity_type.vendor_id); + writer->write_uint32(writer, this->identity_type.type); + writer->write_data32(writer, this->identity_value); + writer->write_uint32(writer, this->subject_type.vendor_id); + writer->write_uint32(writer, this->subject_type.type); + writer->write_uint32(writer, this->auth_type.vendor_id); + writer->write_uint32(writer, this->auth_type.type); +} + +METHOD(tncif_identity_t, process, bool, + private_tncif_identity_t *this, bio_reader_t *reader) +{ + u_int8_t reserved; + u_int32_t vendor_id, type; + chunk_t identity_value; + + if (reader->remaining(reader) < TNCIF_IDENTITY_MIN_SIZE) + { + return FALSE; + } + reader->read_uint8 (reader, &reserved); + reader->read_uint24(reader, &vendor_id); + reader->read_uint32(reader, &type); + this->identity_type = pen_type_create(vendor_id, type); + + if (!reader->read_data32(reader, &identity_value) || + reader->remaining(reader) < 16) + { + return FALSE; + } + this->identity_value = chunk_clone(identity_value); + + reader->read_uint8 (reader, &reserved); + reader->read_uint24(reader, &vendor_id); + reader->read_uint32(reader, &type); + this->subject_type = pen_type_create(vendor_id, type); + + reader->read_uint8 (reader, &reserved); + reader->read_uint24(reader, &vendor_id); + reader->read_uint32(reader, &type); + this->auth_type = pen_type_create(vendor_id, type); + + return TRUE; +} + +METHOD(tncif_identity_t, destroy, void, + private_tncif_identity_t *this) +{ + free(this->identity_value.ptr); + free(this); +} + + +/** + * See header + */ +tncif_identity_t *tncif_identity_create_empty(void) +{ + private_tncif_identity_t *this; + + INIT(this, + .public = { + .get_identity_type = _get_identity_type, + .get_identity_value = _get_identity_value, + .get_subject_type = _get_subject_type, + .get_auth_type = _get_auth_type, + .build = _build, + .process = _process, + .destroy = _destroy, + }, + ); + + return &this->public; +} + +/** + * See header + */ +tncif_identity_t *tncif_identity_create(pen_type_t identity_type, + chunk_t identity_value, + pen_type_t subject_type, + pen_type_t auth_type) +{ + private_tncif_identity_t *this; + + this = (private_tncif_identity_t*)tncif_identity_create_empty(); + this->identity_type = identity_type; + this->identity_value = chunk_clone(identity_value); + this->subject_type = subject_type; + this->auth_type = auth_type; + + return &this->public; +} + diff --git a/src/libtncif/tncif_identity.h b/src/libtncif/tncif_identity.h new file mode 100644 index 000000000..3ef0dd4f0 --- /dev/null +++ b/src/libtncif/tncif_identity.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 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 libtncif libtncif + * + * @addtogroup libtncif + * TNC interface definitions + * + * @defgroup tnc_identities tnc_identities + * @{ @ingroup libtncif + */ + +#ifndef TNCIF_IDENTITY_H_ +#define TNCIF_IDENTITY_H_ + +#include <library.h> + +#include <pen/pen.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> + +#define TNCIF_IDENTITY_MIN_SIZE 28 + +typedef struct tncif_identity_t tncif_identity_t; + +/** + * Public interface of a TNC Identity object + */ +struct tncif_identity_t { + + /** + * Get the TNC Identity Type + * + * @return TNC Identity Type + */ + pen_type_t (*get_identity_type)(tncif_identity_t *this); + + /** + * Get the TNC Identity Value + * + * @return TNC Identity Value + */ + chunk_t (*get_identity_value)(tncif_identity_t *this); + + /** + * Get the TNC Subject Type + * + * @return TNC Subject Type + */ + pen_type_t (*get_subject_type)(tncif_identity_t *this); + + /** + * Get the TNC Authentication Type + * + * @return TNC Authentication Type + */ + pen_type_t (*get_auth_type)(tncif_identity_t *this); + + /** + * Build the IF-IMV TNC Identity attribute encoding + * + * @param writer writer to write encoded data to + */ + void (*build)(tncif_identity_t *this, bio_writer_t *writer); + + /** + * Process the IF-IMV TNC Identity attribute encoding + * + * @param reader reader to read encoded data from + * @return TRUE if successful + */ + bool (*process)(tncif_identity_t *this, bio_reader_t *reader); + + /** + * Destroys a tncif_identity_t object. + */ + void (*destroy)(tncif_identity_t *this); + +}; + +/** + * Create an empty TNC Identity object + */ +tncif_identity_t* tncif_identity_create_empty(void); + +/** + * Create an TNC Identity object from its components + * + * @param identity_type TNC Identity Type + * @param identity_value TNC Identity Value + * @param subject_type TNC Subject Type + * @param auth_type TNC Authentication Type + */ +tncif_identity_t* tncif_identity_create(pen_type_t identity_type, + chunk_t identity_value, + pen_type_t subject_type, + pen_type_t auth_type); + +#endif /** TNCIF_IDENTITY_H_ @}*/ diff --git a/src/libtncif/tncif_names.c b/src/libtncif/tncif_names.c index c108776ec..ac948c8ba 100644 --- a/src/libtncif/tncif_names.c +++ b/src/libtncif/tncif_names.c @@ -45,3 +45,20 @@ ENUM(TNC_IMV_Evaluation_Result_names, "error", "don't know" ); + +ENUM(TNC_Subject_names, + TNC_SUBJECT_UNKNOWN, + TNC_SUBJECT_USER, + "unknown", + "machine", + "user" +); + +ENUM(TNC_Authentication_names, + TNC_AUTH_UNKNOWN, + TNC_AUTH_SIM, + "unknown method", + "certificate", + "password", + "SIM card" +); diff --git a/src/libtncif/tncif_names.h b/src/libtncif/tncif_names.h index 9b50a34e9..75458f960 100644 --- a/src/libtncif/tncif_names.h +++ b/src/libtncif/tncif_names.h @@ -30,5 +30,7 @@ extern enum_name_t *TNC_Connection_State_names; extern enum_name_t *TNC_IMV_Action_Recommendation_names; extern enum_name_t *TNC_IMV_Evaluation_Result_names; +extern enum_name_t *TNC_Subject_names; +extern enum_name_t *TNC_Authentication_names; #endif /** TNCIF_NAME_H_ @}*/ diff --git a/src/libtncif/tncifimv.h b/src/libtncif/tncifimv.h index 3c9db0055..6bce8b4e4 100644 --- a/src/libtncif/tncifimv.h +++ b/src/libtncif/tncifimv.h @@ -209,6 +209,31 @@ typedef TNC_Result (*TNC_IMV_ProvideBindFunctionPointer)( #define TNC_ATTRIBUTEID_SOH ((TNC_AttributeID) 0x00559706) #define TNC_ATTRIBUTEID_SSOH ((TNC_AttributeID) 0x00559707) #define TNC_ATTRIBUTEID_PRIMARY_IMV_ID ((TNC_AttributeID) 0x00559710) +#define TNC_ATTRIBUTEID_AR_IDENTITIES ((TNC_AttributeID) 0x00559712) + +/* TNC Identity Types */ + +#define TNC_ID_UNKNOWN 0 +#define TNC_ID_IPV4_ADDR 1 +#define TNC_ID_IPV6_ADDR 2 +#define TNC_ID_FQDN 3 +#define TNC_ID_RFC822_ADDR 4 +#define TNC_ID_USER_NAME 5 +#define TNC_ID_DER_ASN1_DN 6 +#define TNC_ID_DER_ASN1_GN 7 + +/* TNC Subject Types */ + +#define TNC_SUBJECT_UNKNOWN 0 +#define TNC_SUBJECT_MACHINE 1 +#define TNC_SUBJECT_USER 2 + +/* TNC Authentication Types */ + +#define TNC_AUTH_UNKNOWN 0 +#define TNC_AUTH_CERT 1 +#define TNC_AUTH_PASSWORD 2 +#define TNC_AUTH_SIM 3 /* IMV Functions */ diff --git a/src/pki/commands/pub.c b/src/pki/commands/pub.c index 30078a8fa..9912061f4 100644 --- a/src/pki/commands/pub.c +++ b/src/pki/commands/pub.c @@ -158,7 +158,7 @@ static void __attribute__ ((constructor))reg() pub, 'p', "pub", "extract the public key from a private key/certificate", {"[--in file|--keyid hex] [--type rsa|ecdsa|pkcs10|x509]", - "[--outform der|pem|pgp]"}, + "[--outform der|pem|pgp|dnskey]"}, { {"help", 'h', 0, "show usage information"}, {"in", 'i', 1, "input file, default: stdin"}, diff --git a/src/pki/pki.c b/src/pki/pki.c index 3f77c5e8d..429517b92 100644 --- a/src/pki/pki.c +++ b/src/pki/pki.c @@ -76,6 +76,17 @@ bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type) return FALSE; } } + else if (streq(form, "dnskey")) + { + switch (type) + { + case CRED_PUBLIC_KEY: + *enc =PUBKEY_DNSKEY; + return TRUE; + default: + return FALSE; + } + } return FALSE; } diff --git a/src/scepclient/scep.c b/src/scepclient/scep.c index f2090274c..3fdcd6c28 100644 --- a/src/scepclient/scep.c +++ b/src/scepclient/scep.c @@ -151,8 +151,7 @@ void scep_generate_transaction_id(public_key_t *key, chunk_t *transID, chunk_t digest = chunk_alloca(HASH_SIZE_MD5); chunk_t keyEncoding = chunk_empty, keyInfo; hasher_t *hasher; - bool msb_set; - u_char *pos; + int zeros = 0, msb_set = 0; key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding); @@ -168,20 +167,27 @@ void scep_generate_transaction_id(public_key_t *key, chunk_t *transID, DESTROY_IF(hasher); free(keyInfo.ptr); - /* is the most significant bit of the digest set? */ - msb_set = (*digest.ptr & 0x80) == 0x80; - - /* allocate space for the serialNumber */ - serialNumber->len = msb_set + digest.len; - serialNumber->ptr = malloc(serialNumber->len); - - /* the serial number as the two's complement of the digest */ - pos = serialNumber->ptr; + /* the serialNumber should be valid ASN1 integer content: + * remove leading zeros, add one if MSB is set (two's complement) */ + while (zeros < digest.len) + { + if (digest.ptr[zeros]) + { + if (digest.ptr[zeros] & 0x80) + { + msb_set = 1; + } + break; + } + zeros++; + } + *serialNumber = chunk_alloc(digest.len - zeros + msb_set); if (msb_set) { - *pos++ = 0x00; + serialNumber->ptr[0] = 0x00; } - memcpy(pos, digest.ptr, digest.len); + memcpy(serialNumber->ptr + msb_set, digest.ptr + zeros, + digest.len - zeros); /* the transaction id is the serial number in hex format */ *transID = chunk_to_hex(digest, NULL, TRUE); @@ -333,7 +339,7 @@ static char* escape_http_request(chunk_t req) * Send a SCEP request via HTTP and wait for a response */ bool scep_http_request(const char *url, chunk_t msg, scep_op_t op, - bool http_get_request, chunk_t *response) + bool http_get_request, u_int timeout, chunk_t *response) { int len; status_t status; @@ -361,6 +367,7 @@ bool scep_http_request(const char *url, chunk_t msg, scep_op_t op, status = lib->fetcher->fetch(lib->fetcher, complete_url, response, FETCH_HTTP_VERSION_1_0, + FETCH_TIMEOUT, timeout, FETCH_REQUEST_HEADER, "Pragma:", FETCH_REQUEST_HEADER, "Host:", FETCH_REQUEST_HEADER, "Accept:", @@ -375,6 +382,7 @@ bool scep_http_request(const char *url, chunk_t msg, scep_op_t op, status = lib->fetcher->fetch(lib->fetcher, complete_url, response, FETCH_HTTP_VERSION_1_0, + FETCH_TIMEOUT, timeout, FETCH_REQUEST_DATA, msg, FETCH_REQUEST_TYPE, "", FETCH_REQUEST_HEADER, "Expect:", @@ -403,6 +411,7 @@ bool scep_http_request(const char *url, chunk_t msg, scep_op_t op, status = lib->fetcher->fetch(lib->fetcher, complete_url, response, FETCH_HTTP_VERSION_1_0, + FETCH_TIMEOUT, timeout, FETCH_END); } diff --git a/src/scepclient/scep.h b/src/scepclient/scep.h index 30551d2db..ec8fa6515 100644 --- a/src/scepclient/scep.h +++ b/src/scepclient/scep.h @@ -79,7 +79,7 @@ chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg, size_t key_size, certificate_t *signer_cert, hash_algorithm_t digest_alg, private_key_t *private_key); bool scep_http_request(const char *url, chunk_t message, scep_op_t op, - bool http_get_request, chunk_t *response); + bool http_get_request, u_int timeout, chunk_t *response); err_t scep_parse_response(chunk_t response, chunk_t transID, container_t **out, scep_attributes_t *attrs); diff --git a/src/scepclient/scepclient.c b/src/scepclient/scepclient.c index 83b5d6219..26f210d12 100644 --- a/src/scepclient/scepclient.c +++ b/src/scepclient/scepclient.c @@ -113,6 +113,9 @@ long crl_check_interval = 0; /* by default pluto logs out after every smartcard use */ bool pkcs11_keep_state = FALSE; +/* by default HTTP fetch timeout is 30s */ +static u_int http_timeout = 30; + /* options read by optionsfrom */ options_t *options; @@ -344,6 +347,7 @@ static void usage(const char *message) " - if no filename is given, default is used\n" " --optionsfrom (-+) <filename> reads additional options from given file\n" " --force (-f) force existing file(s)\n" + " --httptimeout (-T) timeout for HTTP operations (default: 30s)\n" "\n" "Options for key generation (pkcs1):\n" " --keylength (-k) <bits> key length for RSA key generation\n" @@ -518,6 +522,7 @@ int main(int argc, char **argv) { "in", required_argument, NULL, 'i' }, { "out", required_argument, NULL, 'o' }, { "force", no_argument, NULL, 'f' }, + { "httptimeout", required_argument, NULL, 'T' }, { "keylength", required_argument, NULL, 'k' }, { "dn", required_argument, NULL, 'd' }, { "days", required_argument, NULL, 'D' }, @@ -662,6 +667,14 @@ int main(int argc, char **argv) force = TRUE; continue; + case 'T': /* --httptimeout */ + http_timeout = atoi(optarg); + if (http_timeout <= 0) + { + usage("invalid httptimeout specified"); + } + continue; + case '+': /* --optionsfrom <filename> */ if (!options->from(options, optarg, &argc, &argv, optind)) { @@ -939,7 +952,8 @@ int main(int argc, char **argv) pkcs7_t *pkcs7; if (!scep_http_request(scep_url, chunk_create(ca_name, strlen(ca_name)), - SCEP_GET_CA_CERT, http_get_request, &scep_response)) + SCEP_GET_CA_CERT, http_get_request, + http_timeout, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } @@ -1317,7 +1331,7 @@ int main(int argc, char **argv) creds->add_cert(creds, TRUE, x509_ca_sig->get_ref(x509_ca_sig)); if (!scep_http_request(scep_url, pkcs7, SCEP_PKI_OPERATION, - http_get_request, &scep_response)) + http_get_request, http_timeout, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } @@ -1337,7 +1351,7 @@ int main(int argc, char **argv) poll_start = time_monotonic(NULL); issuerAndSubject = asn1_wrap(ASN1_SEQUENCE, "cc", issuer->get_encoding(issuer), - subject); + subject->get_encoding(subject)); } while (attrs.pkiStatus == SCEP_PENDING) { @@ -1367,7 +1381,7 @@ int main(int argc, char **argv) exit_scepclient("failed to build scep request"); } if (!scep_http_request(scep_url, getCertInitial, SCEP_PKI_OPERATION, - http_get_request, &scep_response)) + http_get_request, http_timeout, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } @@ -1458,5 +1472,3 @@ int main(int argc, char **argv) exit_scepclient(NULL); return -1; /* should never be reached */ } - - diff --git a/src/starter/args.c b/src/starter/args.c index 390062a99..5fbf51856 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -33,6 +33,7 @@ typedef enum { ARG_TIME, ARG_ULNG, ARG_ULLI, + ARG_UBIN, ARG_PCNT, ARG_STR, ARG_LST, @@ -146,6 +147,7 @@ static const token_info_t token_info[] = { ARG_MISC, 0, NULL /* KW_MOBIKE */ }, { ARG_MISC, 0, NULL /* KW_FORCEENCAPS */ }, { ARG_ENUM, offsetof(starter_conn_t, fragmentation), LST_fragmentation }, + { ARG_UBIN, offsetof(starter_conn_t, ikedscp), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_ike_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_ipsec_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_rekey_margin), NULL }, @@ -399,6 +401,21 @@ bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw, char *base, } } break; + case ARG_UBIN: + { + char *endptr; + u_int *u = (u_int *)p; + + *u = strtoul(kw->value, &endptr, 2); + + if (*endptr != '\0') + { + DBG1(DBG_APP, "# bad binary value: %s=%s", kw->entry->name, + kw->value); + return FALSE; + } + } + break; case ARG_TIME: { char *endptr; diff --git a/src/starter/confread.h b/src/starter/confread.h index 26a3568d4..0690bed4e 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -149,6 +149,7 @@ struct starter_conn { ipsec_mode_t mode; bool proxy_mode; fragmentation_t fragmentation; + u_int ikedscp; sa_option_t options; time_t sa_ike_life_seconds; time_t sa_ipsec_life_seconds; diff --git a/src/starter/keywords.h b/src/starter/keywords.h index f776f33c9..4a96a418c 100644 --- a/src/starter/keywords.h +++ b/src/starter/keywords.h @@ -43,6 +43,7 @@ typedef enum { KW_MOBIKE, KW_FORCEENCAPS, KW_FRAGMENTATION, + KW_IKEDSCP, KW_IKELIFETIME, KW_KEYLIFE, KW_REKEYMARGIN, @@ -186,4 +187,3 @@ typedef enum { } kw_token_t; #endif /* _KEYWORDS_H_ */ - diff --git a/src/starter/keywords.txt b/src/starter/keywords.txt index 1f1641287..cd964b0e3 100644 --- a/src/starter/keywords.txt +++ b/src/starter/keywords.txt @@ -41,6 +41,7 @@ aaa_identity, KW_AAA_IDENTITY mobike, KW_MOBIKE forceencaps, KW_FORCEENCAPS fragmentation, KW_FRAGMENTATION +ikedscp, KW_IKEDSCP, ikelifetime, KW_IKELIFETIME lifetime, KW_KEYLIFE keylife, KW_KEYLIFE diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 39c59eb15..cc447c41f 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -182,6 +182,7 @@ int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn) msg.add_conn.mobike = conn->options & SA_OPTION_MOBIKE; msg.add_conn.force_encap = conn->options & SA_OPTION_FORCE_ENCAP; msg.add_conn.fragmentation = conn->fragmentation; + msg.add_conn.ikedscp = conn->ikedscp; msg.add_conn.ipcomp = conn->options & SA_OPTION_COMPRESS; msg.add_conn.install_policy = conn->install_policy; msg.add_conn.aggressive = conn->aggressive; diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 3eaa327ce..a9c6f2369 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -263,6 +263,7 @@ struct stroke_msg_t { int close_action; u_int32_t reqid; u_int32_t tfc; + u_int8_t ikedscp; crl_policy_t crl_policy; int unique; |