diff options
Diffstat (limited to 'src')
310 files changed, 21743 insertions, 5263 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 5e85a5f88..1440de20f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,10 @@ if USE_TLS SUBDIRS += libtls endif +if USE_RADIUS + SUBDIRS += libradius +endif + if USE_LIBTNCIF SUBDIRS += libtncif endif @@ -24,14 +28,14 @@ if USE_LIBTNCCS SUBDIRS += libtnccs endif -if USE_PTS - SUBDIRS += libpts -endif - if USE_IMCV SUBDIRS += libimcv endif +if USE_PTS + SUBDIRS += libpts +endif + if USE_LIBCHARON SUBDIRS += libcharon endif diff --git a/src/charon/Android.mk b/src/charon/Android.mk index 491d7f946..eb7eca9dd 100644 --- a/src/charon/Android.mk +++ b/src/charon/Android.mk @@ -17,6 +17,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := charon +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/charon/charon.c b/src/charon/charon.c index 141403b89..7a269d7f0 100644 --- a/src/charon/charon.c +++ b/src/charon/charon.c @@ -426,7 +426,7 @@ static void usage(const char *msg) " [--version]\n" " [--use-syslog]\n" " [--debug-<type> <level>]\n" - " <type>: log context type (dmn|mgr|ike|chd|job|cfg|knl|net|enc|tnc|imc|imv|pts|tls|lib)\n" + " <type>: log context type (dmn|mgr|ike|chd|job|cfg|knl|net|asn|enc|tnc|imc|imv|pts|tls|lib)\n" " <level>: log verbosity (-1 = silent, 0 = audit, 1 = control,\n" " 2 = controlmore, 3 = raw, 4 = private)\n" "\n" @@ -497,6 +497,7 @@ int main(int argc, char *argv[]) { "debug-cfg", required_argument, &group, DBG_CFG }, { "debug-knl", required_argument, &group, DBG_KNL }, { "debug-net", required_argument, &group, DBG_NET }, + { "debug-asn", required_argument, &group, DBG_ASN }, { "debug-enc", required_argument, &group, DBG_ENC }, { "debug-tnc", required_argument, &group, DBG_TNC }, { "debug-imc", required_argument, &group, DBG_IMC }, diff --git a/src/checksum/Makefile.am b/src/checksum/Makefile.am index 036a63715..58292a45a 100644 --- a/src/checksum/Makefile.am +++ b/src/checksum/Makefile.am @@ -45,6 +45,11 @@ if USE_TLS libs += $(DESTDIR)$(ipseclibdir)/libtls.so endif +if USE_RADIUS + deps += $(top_builddir)/src/libradius/libradius.la + libs += $(DESTDIR)$(ipseclibdir)/libradius.so +endif + if USE_LIBTNCCS deps += $(top_builddir)/src/libtnccs/libtnccs.la libs += $(DESTDIR)$(ipseclibdir)/libtnccs.so @@ -55,6 +60,16 @@ if USE_SIMAKA libs += $(DESTDIR)$(ipseclibdir)/libsimaka.so endif +if USE_IMCV + deps += $(top_builddir)/src/libimcv/libimcv.la + libs += $(DESTDIR)$(ipseclibdir)/libimcv.so +endif + +if USE_PTS + deps += $(top_builddir)/src/libpts/libpts.la + libs += $(DESTDIR)$(ipseclibdir)/libpts.so +endif + if USE_CHARON deps += $(top_builddir)/src/libcharon/libcharon.la libs += $(DESTDIR)$(ipseclibdir)/libcharon.so @@ -79,6 +94,10 @@ if USE_ATTR_SQL exes += $(top_builddir)/src/libhydra/plugins/attr_sql/.libs/pool endif +if USE_IMV_ATTESTATION + exes += $(top_builddir)/src/libpts/plugins/imv_attestation/.libs/attest +endif + checksum.c : checksum_builder $(deps) $(exes) ./checksum_builder $(libs) $(exes) > checksum.c diff --git a/src/checksum/checksum_builder.c b/src/checksum/checksum_builder.c index 8311d9a39..670ec76bd 100644 --- a/src/checksum/checksum_builder.c +++ b/src/checksum/checksum_builder.c @@ -151,7 +151,8 @@ int main(int argc, char* argv[]) printf("\n"); printf("integrity_checksum_t checksums[] = {\n"); fprintf(stderr, "integrity test data:\n"); - fprintf(stderr, "module name, file size / checksum segment size / checksum\n"); + fprintf(stderr, "module name, file size / checksum " + "segment size / checksum\n"); for (i = 1; i < argc; i++) { build_binary_checksum(argv[i]); diff --git a/src/ipsec/Android.mk b/src/ipsec/Android.mk index b6a7c714d..d134f7fd2 100644 --- a/src/ipsec/Android.mk +++ b/src/ipsec/Android.mk @@ -5,6 +5,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := ipsec +LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := EXECUTABLES GEN := $(local-intermediates-dir)/ipsec diff --git a/src/ipsec/Makefile.am b/src/ipsec/Makefile.am index 8420e1ace..bbf009721 100644 --- a/src/ipsec/Makefile.am +++ b/src/ipsec/Makefile.am @@ -1,7 +1,7 @@ sbin_SCRIPTS = ipsec CLEANFILES = ipsec ipsec.8 dist_man8_MANS = ipsec.8 -EXTRA_DIST = ipsec.in ipsec.8.in +EXTRA_DIST = ipsec.in ipsec.8.in Android.mk ipsec.8 : ipsec.8.in sed \ diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 3ba8307c8..f98d36a61 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -132,6 +132,8 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libsimaka/ LOCAL_SRC_FILES += $(addprefix ../libsimaka/, \ simaka_message.h simaka_message.c \ simaka_crypto.h simaka_crypto.c \ + simaka_manager.h simaka_manager.c \ + simaka_card.h simaka_provider.h simaka_hooks.h \ ) endif @@ -163,6 +165,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) \ LOCAL_MODULE := libcharon +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index d5e139412..a322b0cce 100755 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -360,6 +360,13 @@ if MONOLITHIC endif endif +if USE_RADIUS +if MONOLITHIC + # otherwise this library is linked to eap_radius + libcharon_la_LIBADD += $(top_builddir)/src/libradius/libradius.la +endif +endif + if USE_TNC_IFMAP SUBDIRS += plugins/tnc_ifmap if MONOLITHIC @@ -367,6 +374,13 @@ if MONOLITHIC endif endif +if USE_TNC_PDP + SUBDIRS += plugins/tnc_pdp +if MONOLITHIC + libcharon_la_LIBADD += plugins/tnc_pdp/libstrongswan-tnc-pdp.la +endif +endif + if USE_TNC_IMC SUBDIRS += plugins/tnc_imc if MONOLITHIC @@ -500,6 +514,13 @@ if MONOLITHIC endif endif +if USE_RADATTR + SUBDIRS += plugins/radattr +if MONOLITHIC + libcharon_la_LIBADD += plugins/radattr/libstrongswan-radattr.la +endif +endif + if USE_UCI SUBDIRS += plugins/uci if MONOLITHIC diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index 343063be5..575627206 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -111,6 +111,12 @@ static void destroy(private_daemon_t *this) } DESTROY_IF(this->public.receiver); DESTROY_IF(this->public.sender); +#ifdef ME + DESTROY_IF(this->public.connect_manager); + DESTROY_IF(this->public.mediation_manager); +#endif /* ME */ + /* make sure the cache is clear before unloading plugins */ + lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); /* unload plugins to release threads */ lib->plugins->unload(lib->plugins); #ifdef CAPABILITIES_LIBCAP @@ -123,10 +129,6 @@ static void destroy(private_daemon_t *this) DESTROY_IF(this->public.controller); DESTROY_IF(this->public.eap); DESTROY_IF(this->public.xauth); -#ifdef ME - DESTROY_IF(this->public.connect_manager); - DESTROY_IF(this->public.mediation_manager); -#endif /* ME */ DESTROY_IF(this->public.backends); DESTROY_IF(this->public.socket); @@ -200,27 +202,6 @@ METHOD(daemon_t, start, void, DEFAULT_THREADS)); } -/** - * Log loaded plugins - */ -static void print_plugins() -{ - char buf[512]; - int len = 0; - enumerator_t *enumerator; - plugin_t *plugin; - - buf[0] = '\0'; - enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); - while (len < sizeof(buf) && enumerator->enumerate(enumerator, &plugin, NULL)) - { - len += snprintf(&buf[len], sizeof(buf)-len, "%s ", - plugin->get_name(plugin)); - } - enumerator->destroy(enumerator); - DBG1(DBG_DMN, "loaded plugins: %s", buf); -} - METHOD(daemon_t, initialize, bool, private_daemon_t *this) { @@ -241,8 +222,8 @@ METHOD(daemon_t, initialize, bool, { return FALSE; } - - print_plugins(); + DBG1(DBG_DMN, "loaded plugins: %s", + lib->plugins->loaded_plugins(lib->plugins)); this->public.ike_sa_manager = ike_sa_manager_create(); if (this->public.ike_sa_manager == NULL) diff --git a/src/libcharon/encoding/payloads/notify_payload.c b/src/libcharon/encoding/payloads/notify_payload.c index ade2d945f..411534491 100755 --- a/src/libcharon/encoding/payloads/notify_payload.c +++ b/src/libcharon/encoding/payloads/notify_payload.c @@ -115,15 +115,16 @@ ENUM_NEXT(notify_type_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THE "UNITY_LOAD_BALANCE"); ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE, "USE_BEET_MODE"); -ENUM_NEXT(notify_type_names, ME_MEDIATION, ME_RESPONSE, USE_BEET_MODE, +ENUM_NEXT(notify_type_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE, "ME_MEDIATION", "ME_ENDPOINT", "ME_CALLBACK", "ME_CONNECTID", "ME_CONNECTKEY", "ME_CONNECTAUTH", - "ME_RESPONSE"); -ENUM_END(notify_type_names, ME_RESPONSE); + "ME_RESPONSE", + "RADIUS_ATTRIBUTE",); +ENUM_END(notify_type_names, RADIUS_ATTRIBUTE); ENUM_BEGIN(notify_type_short_names, UNSUPPORTED_CRITICAL_PAYLOAD, UNSUPPORTED_CRITICAL_PAYLOAD, @@ -216,15 +217,16 @@ ENUM_NEXT(notify_type_short_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R "UNITY_LB"); ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE, "BEET_MODE"); -ENUM_NEXT(notify_type_short_names, ME_MEDIATION, ME_RESPONSE, USE_BEET_MODE, +ENUM_NEXT(notify_type_short_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE, "ME_MED", "ME_EP", "ME_CB", "ME_CID", "ME_CKEY", "ME_CAUTH", - "ME_R"); -ENUM_END(notify_type_short_names, ME_RESPONSE); + "ME_R", + "RADIUS"); +ENUM_END(notify_type_short_names, RADIUS_ATTRIBUTE); typedef struct private_notify_payload_t private_notify_payload_t; diff --git a/src/libcharon/encoding/payloads/notify_payload.h b/src/libcharon/encoding/payloads/notify_payload.h index 58d85ffae..07fbcb49b 100755 --- a/src/libcharon/encoding/payloads/notify_payload.h +++ b/src/libcharon/encoding/payloads/notify_payload.h @@ -154,7 +154,9 @@ enum notify_type_t { ME_CONNECTID = 40965, ME_CONNECTKEY = 40966, ME_CONNECTAUTH = 40967, - ME_RESPONSE = 40968 + ME_RESPONSE = 40968, + /* RADIUS attribute received/to send to a AAA backend */ + RADIUS_ATTRIBUTE = 40969, }; /** diff --git a/src/libcharon/plugins/eap_peap/eap_peap.c b/src/libcharon/plugins/eap_peap/eap_peap.c index 5bae0fa9b..bd426bba7 100644 --- a/src/libcharon/plugins/eap_peap/eap_peap.c +++ b/src/libcharon/plugins/eap_peap/eap_peap.c @@ -166,7 +166,8 @@ static eap_peap_t *eap_peap_create(private_eap_peap_t * this, "charon.plugins.eap-peap.max_message_count", MAX_MESSAGE_COUNT); include_length = lib->settings->get_bool(lib->settings, "charon.plugins.eap-peap.include_length", FALSE); - tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_PEAP, application); + tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_PEAP, + application, NULL); this->tls_eap = tls_eap_create(EAP_PEAP, tls, frag_size, max_msg_count, include_length); if (!this->tls_eap) diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am index afc50bced..181497ab5 100644 --- a/src/libcharon/plugins/eap_radius/Makefile.am +++ b/src/libcharon/plugins/eap_radius/Makefile.am @@ -1,21 +1,21 @@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \ - -I$(top_srcdir)/src/libcharon + -I$(top_srcdir)/src/libcharon -I$(top_srcdir)/src/libradius AM_CFLAGS = -rdynamic if MONOLITHIC noinst_LTLIBRARIES = libstrongswan-eap-radius.la else +libstrongswan_eap_radius_la_LIBADD = $(top_builddir)/src/libradius/libradius.la plugin_LTLIBRARIES = libstrongswan-eap-radius.la endif libstrongswan_eap_radius_la_SOURCES = \ eap_radius_plugin.h eap_radius_plugin.c \ eap_radius.h eap_radius.c \ - radius_server.h radius_server.c \ - radius_socket.h radius_socket.c \ - radius_client.h radius_client.c \ - radius_message.h radius_message.c + eap_radius_accounting.h eap_radius_accounting.c \ + eap_radius_dae.h eap_radius_dae.c \ + eap_radius_forward.h eap_radius_forward.c libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c index dfe0e2e09..c0a3703b6 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius.c +++ b/src/libcharon/plugins/eap_radius/eap_radius.c @@ -14,14 +14,14 @@ */ #include "eap_radius.h" +#include "eap_radius_plugin.h" +#include "eap_radius_forward.h" -#include "radius_message.h" -#include "radius_client.h" +#include <radius_message.h> +#include <radius_client.h> #include <daemon.h> -#define TUNNEL_TYPE_ESP 9 - typedef struct private_eap_radius_t private_eap_radius_t; /** @@ -162,7 +162,7 @@ METHOD(eap_method_t, initiate, status_t, status_t status = FAILED; chunk_t username; - request = radius_message_create_request(); + request = radius_message_create(RMC_ACCESS_REQUEST); username = chunk_create(this->id_prefix, strlen(this->id_prefix)); username = chunk_cata("cc", username, this->peer->get_encoding(this->peer)); request->add(request, RAT_USER_NAME, username); @@ -175,16 +175,22 @@ METHOD(eap_method_t, initiate, status_t, { add_eap_identity(this, request); } + eap_radius_forward_from_ike(request); response = this->client->request(this->client, request); if (response) { + eap_radius_forward_to_ike(response); if (radius2ike(this, response, out)) { status = NEED_MORE; } response->destroy(response); } + else + { + charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + } request->destroy(request); return status; } @@ -253,7 +259,7 @@ static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg) tunnel_type = untoh32(data.ptr); DBG1(DBG_IKE, "received RADIUS attribute Tunnel-Type: " "tag = %u, value = %u", tunnel_tag, tunnel_type); - is_esp_tunnel = (tunnel_type == TUNNEL_TYPE_ESP); + is_esp_tunnel = (tunnel_type == RADIUS_TUNNEL_TYPE_ESP); break; case RAT_FILTER_ID: filter_id = data; @@ -282,6 +288,31 @@ static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg) } } +/** + * Handle Session-Timeout attribte + */ +static void process_timeout(private_eap_radius_t *this, radius_message_t *msg) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + chunk_t data; + int type; + + enumerator = msg->create_enumerator(msg); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_SESSION_TIMEOUT && data.len == 4) + { + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + ike_sa->set_auth_lifetime(ike_sa, untoh32(data.ptr)); + } + } + } + enumerator->destroy(enumerator); +} + METHOD(eap_method_t, process, status_t, private_eap_radius_t *this, eap_payload_t *in, eap_payload_t **out) { @@ -289,22 +320,25 @@ METHOD(eap_method_t, process, status_t, status_t status = FAILED; chunk_t data; - request = radius_message_create_request(); + request = radius_message_create(RMC_ACCESS_REQUEST); request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer)); data = in->get_data(in); DBG3(DBG_IKE, "%N payload %B", eap_type_names, this->type, &data); - - /* fragment data suitable for RADIUS (not more than 253 bytes) */ - while (data.len > 253) + + /* fragment data suitable for RADIUS */ + while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE) { - request->add(request, RAT_EAP_MESSAGE, chunk_create(data.ptr, 253)); - data = chunk_skip(data, 253); + request->add(request, RAT_EAP_MESSAGE, + chunk_create(data.ptr,MAX_RADIUS_ATTRIBUTE_SIZE)); + data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE); } request->add(request, RAT_EAP_MESSAGE, data); + eap_radius_forward_from_ike(request); response = this->client->request(this->client, request); if (response) { + eap_radius_forward_to_ike(response); switch (response->get_code(response)) { case RMC_ACCESS_CHALLENGE: @@ -324,6 +358,7 @@ METHOD(eap_method_t, process, status_t, { process_filter_id(this, response); } + process_timeout(this, response); DBG1(DBG_IKE, "RADIUS authentication of '%Y' successful", this->peer); status = SUCCESS; @@ -427,7 +462,7 @@ eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer "charon.plugins.eap-radius.filter_id", FALSE), ); - this->client = radius_client_create(); + this->client = eap_radius_create_client(); if (!this->client) { free(this); diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c new file mode 100644 index 000000000..243c76304 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -0,0 +1,339 @@ +/* + * 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 "eap_radius_accounting.h" +#include "eap_radius_plugin.h" + +#include <time.h> + +#include <radius_message.h> +#include <radius_client.h> +#include <daemon.h> +#include <utils/hashtable.h> +#include <threading/mutex.h> + +typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t; + +/** + * Private data of an eap_radius_accounting_t object. + */ +struct private_eap_radius_accounting_t { + + /** + * Public eap_radius_accounting_t interface. + */ + eap_radius_accounting_t public; + + /** + * Hashtable with sessions, IKE_SA unique id => entry_t + */ + hashtable_t *sessions; + + /** + * Mutex to lock sessions + */ + mutex_t *mutex; + + /** + * Session ID prefix + */ + u_int32_t prefix; +}; + +/** + * Hashtable entry with usage stats + */ +typedef struct { + /** RADIUS accounting session ID */ + char sid[16]; + /** number of octets sent */ + u_int64_t sent; + /** number of octets received */ + u_int64_t received; + /** session creation time */ + time_t created; +} entry_t; + +/** + * Accounting message status types + */ +typedef enum { + ACCT_STATUS_START = 1, + ACCT_STATUS_STOP = 2, + ACCT_STATUS_INTERIM_UPDATE = 3, + ACCT_STATUS_ACCOUNTING_ON = 7, + ACCT_STATUS_ACCOUNTING_OFF = 8, +} radius_acct_status_t; + +/** + * Hashtable hash function + */ +static u_int hash(uintptr_t key) +{ + return key; +} + +/** + * Hashtable equals function + */ +static bool equals(uintptr_t a, uintptr_t b) +{ + return a == b; +} + +/** + * Update usage counter when a CHILD_SA rekeys/goes down + */ +static void update_usage(private_eap_radius_accounting_t *this, + ike_sa_t *ike_sa, child_sa_t *child_sa) +{ + u_int64_t sent, received; + entry_t *entry; + + child_sa->get_usestats(child_sa, FALSE, NULL, &sent); + child_sa->get_usestats(child_sa, TRUE, NULL, &received); + + this->mutex->lock(this->mutex); + entry = this->sessions->get(this->sessions, + (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa)); + if (entry) + { + entry->sent += sent; + entry->received += received; + } + this->mutex->unlock(this->mutex); +} + +/** + * Send a RADIUS message, wait for response + */ +static bool send_message(private_eap_radius_accounting_t *this, + radius_message_t *request) +{ + radius_message_t *response; + radius_client_t *client; + bool ack = FALSE; + + client = eap_radius_create_client(); + if (client) + { + response = client->request(client, request); + if (response) + { + ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE; + response->destroy(response); + } + else + { + charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + } + client->destroy(client); + } + return ack; +} + +/** + * Add common IKE_SA parameters to RADIUS account message + */ +static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) +{ + host_t *vip; + char buf[64]; + chunk_t data; + + snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa)); + message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf))); + snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa)); + message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf))); + vip = ike_sa->get_virtual_ip(ike_sa, FALSE); + if (vip && vip->get_family(vip) == AF_INET) + { + message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip)); + } + if (vip && vip->get_family(vip) == AF_INET6) + { + /* we currently assign /128 prefixes, only (reserved, length) */ + data = chunk_from_chars(0, 128); + data = chunk_cata("cc", data, vip->get_address(vip)); + message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + } +} + +/** + * Send an accounting start message + */ +static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) +{ + radius_message_t *message; + entry_t *entry; + u_int32_t id, value; + + id = ike_sa->get_unique_id(ike_sa); + INIT(entry, + .created = time_monotonic(NULL), + ); + snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id); + + message = radius_message_create(RMC_ACCOUNTING_REQUEST); + value = htonl(ACCT_STATUS_START); + message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); + message->add(message, RAT_ACCT_SESSION_ID, + chunk_create(entry->sid, strlen(entry->sid))); + add_ike_sa_parameters(message, ike_sa); + if (send_message(this, message)) + { + this->mutex->lock(this->mutex); + entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry); + this->mutex->unlock(this->mutex); + free(entry); + } + message->destroy(message); +} + +/** + * Send an account stop message + */ +static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) +{ + radius_message_t *message; + entry_t *entry; + u_int32_t id, value; + + id = ike_sa->get_unique_id(ike_sa); + this->mutex->lock(this->mutex); + entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id); + this->mutex->unlock(this->mutex); + if (entry) + { + message = radius_message_create(RMC_ACCOUNTING_REQUEST); + value = htonl(ACCT_STATUS_STOP); + message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value)); + message->add(message, RAT_ACCT_SESSION_ID, + chunk_create(entry->sid, strlen(entry->sid))); + add_ike_sa_parameters(message, ike_sa); + value = htonl(entry->sent); + message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value)); + value = htonl(entry->sent >> 32); + if (value) + { + message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS, + chunk_from_thing(value)); + } + value = htonl(entry->received); + message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value)); + value = htonl(entry->received >> 32); + if (value) + { + message->add(message, RAT_ACCT_INPUT_GIGAWORDS, + chunk_from_thing(value)); + } + value = htonl(time_monotonic(NULL) - entry->created); + message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value)); + + send_message(this, message); + message->destroy(message); + free(entry); + } +} + +METHOD(listener_t, ike_updown, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up) +{ + if (!up) + { + enumerator_t *enumerator; + child_sa_t *child_sa; + + /* update usage for all children just before sending stop */ + enumerator = ike_sa->create_child_sa_enumerator(ike_sa); + while (enumerator->enumerate(enumerator, &child_sa)) + { + update_usage(this, ike_sa, child_sa); + } + enumerator->destroy(enumerator); + + send_stop(this, ike_sa); + } + return TRUE; +} + +METHOD(listener_t, message_hook, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain) +{ + /* start accounting here, virtual IP now is set */ + if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && + message->get_exchange_type(message) == IKE_AUTH && + !incoming && !message->get_request(message)) + { + send_start(this, ike_sa); + } + return TRUE; +} + +METHOD(listener_t, child_rekey, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, + child_sa_t *old, child_sa_t *new) +{ + update_usage(this, ike_sa, old); + + return TRUE; +} + +METHOD(listener_t, child_updown, bool, + private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up) +{ + if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED) + { + update_usage(this, ike_sa, child_sa); + } + return TRUE; +} + +METHOD(eap_radius_accounting_t, destroy, void, + private_eap_radius_accounting_t *this) +{ + this->mutex->destroy(this->mutex); + this->sessions->destroy(this->sessions); + free(this); +} + +/** + * See header + */ +eap_radius_accounting_t *eap_radius_accounting_create() +{ + private_eap_radius_accounting_t *this; + + INIT(this, + .public = { + .listener = { + .ike_updown = _ike_updown, + .message = _message_hook, + .child_updown = _child_updown, + .child_rekey = _child_rekey, + }, + .destroy = _destroy, + }, + /* use system time as Session ID prefix */ + .prefix = (u_int32_t)time(NULL), + .sessions = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 32), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h new file mode 100644 index 000000000..811a5bb90 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h @@ -0,0 +1,49 @@ +/* + * 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 eap_radius_accounting eap_radius_accounting + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_ACCOUNTING_H_ +#define EAP_RADIUS_ACCOUNTING_H_ + +#include <bus/listeners/listener.h> + +typedef struct eap_radius_accounting_t eap_radius_accounting_t; + +/** + * RADIUS accounting for IKE/IPsec. + */ +struct eap_radius_accounting_t { + + /** + * Implements listener_t. + */ + listener_t listener; + + /** + * Destroy a eap_radius_accounting_t. + */ + void (*destroy)(eap_radius_accounting_t *this); +}; + +/** + * Create a eap_radius_accounting instance. + */ +eap_radius_accounting_t *eap_radius_accounting_create(); + +#endif /** EAP_RADIUS_ACCOUNTING_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_dae.c b/src/libcharon/plugins/eap_radius/eap_radius_dae.c new file mode 100644 index 000000000..5823142cc --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_dae.c @@ -0,0 +1,543 @@ +/* + * 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 "eap_radius_dae.h" + +#include <radius_message.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <unistd.h> +#include <errno.h> + +#include <daemon.h> +#include <threading/thread.h> +#include <processing/jobs/callback_job.h> +#include <processing/jobs/delete_ike_sa_job.h> + +#define RADIUS_DAE_PORT 3799 + +typedef struct private_eap_radius_dae_t private_eap_radius_dae_t; + +/** + * Private data of an eap_radius_dae_t object. + */ +struct private_eap_radius_dae_t { + + /** + * Public eap_radius_dae_t interface. + */ + eap_radius_dae_t public; + + /** + * RADIUS session state + */ + eap_radius_accounting_t *accounting; + + /** + * Socket to listen on authorization extension port + */ + int fd; + + /** + * Listen job + */ + callback_job_t *job; + + /** + * RADIUS shared secret for DAE exchanges + */ + chunk_t secret; + + /** + * MD5 hasher + */ + hasher_t *hasher; + + /** + * HMAC MD5 signer, with secret set + */ + signer_t *signer; + + /** + * List of responses for retransmission, as entry_t + */ + linked_list_t *responses; +}; + +/** + * Entry to store responses for retransmit + */ +typedef struct { + /** stored response */ + radius_message_t *response; + /** client that sent the request */ + host_t *client; +} entry_t; + +/** + * Clean up an entry + */ +static void entry_destroy(entry_t *entry) +{ + entry->response->destroy(entry->response); + entry->client->destroy(entry->client); + free(entry); +} + +/** + * Save/Replace response for retransmission + */ +static void save_retransmit(private_eap_radius_dae_t *this, + radius_message_t *response, host_t *client) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = this->responses->create_enumerator(this->responses); + while (enumerator->enumerate(enumerator, &entry)) + { + if (client->equals(client, entry->client)) + { + entry->response->destroy(entry->response); + entry->response = response; + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!found) + { + INIT(entry, + .response = response, + .client = client->clone(client), + ); + this->responses->insert_first(this->responses, entry); + } +} + +/** + * Send a RADIUS message to client + */ +static void send_message(private_eap_radius_dae_t *this, + radius_message_t *message, host_t *client) +{ + chunk_t data; + + data = message->get_encoding(message); + if (sendto(this->fd, data.ptr, data.len, 0, client->get_sockaddr(client), + *client->get_sockaddr_len(client)) != data.len) + { + DBG1(DBG_CFG, "sending RADIUS DAE response failed: %s", strerror(errno)); + } +} + +/** + * Check if we request is a retransmit, retransmit stored response + */ +static bool send_retransmit(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = this->responses->create_enumerator(this->responses); + while (enumerator->enumerate(enumerator, &entry)) + { + if (client->equals(client, entry->client) && + request->get_identifier(request) == + entry->response->get_identifier(entry->response)) + { + DBG1(DBG_CFG, "received retransmit of RADIUS %N, retransmitting %N " + "to %H", radius_message_code_names, request->get_code(request), + radius_message_code_names, + entry->response->get_code(entry->response), client); + send_message(this, entry->response, client); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + +/** + * Send an ACK/NAK response for a request + */ +static void send_response(private_eap_radius_dae_t *this, + radius_message_t *request, radius_message_code_t code, + host_t *client) +{ + radius_message_t *response; + + response = radius_message_create(code); + response->set_identifier(response, request->get_identifier(request)); + response->sign(response, request->get_authenticator(request), + this->secret, this->hasher, this->signer, NULL, FALSE); + + send_message(this, response, client); + save_retransmit(this, response, client); +} + +/** + * Add all IKE_SAs matching to user to a list + */ +static void add_matching_ike_sas(linked_list_t *list, identification_t *user) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + ike_sa_id_t *id; + + enumerator = charon->ike_sa_manager->create_enumerator( + charon->ike_sa_manager, FALSE); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (user->matches(user, ike_sa->get_other_eap_id(ike_sa))) + { + id = ike_sa->get_id(ike_sa); + list->insert_last(list, id->clone(id)); + } + } + enumerator->destroy(enumerator); +} + +/** + * Get list of IKE_SAs matching a Disconnect/CoA request + */ +static linked_list_t *get_matching_ike_sas(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + identification_t *user; + linked_list_t *ids; + chunk_t data; + int type; + + ids = linked_list_create(); + + enumerator = request->create_enumerator(request); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_USER_NAME && data.len) + { + user = identification_create_from_data(data); + DBG1(DBG_CFG, "received RADIUS DAE %N for %Y from %H", + radius_message_code_names, request->get_code(request), + user, client); + add_matching_ike_sas(ids, user); + user->destroy(user); + } + } + enumerator->destroy(enumerator); + + return ids; +} + +/** + * Process a DAE disconnect request, send response + */ +static void process_disconnect(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + linked_list_t *ids; + ike_sa_id_t *id; + + ids = get_matching_ike_sas(this, request, client); + + if (ids->get_count(ids)) + { + DBG1(DBG_CFG, "closing %d IKE_SA%s matching %N, sending %N", + ids->get_count(ids), ids->get_count(ids) > 1 ? "s" : "", + radius_message_code_names, RMC_DISCONNECT_REQUEST, + radius_message_code_names, RMC_DISCONNECT_ACK); + + enumerator = ids->create_enumerator(ids); + while (enumerator->enumerate(enumerator, &id)) + { + lib->processor->queue_job(lib->processor, (job_t*) + delete_ike_sa_job_create(id, TRUE)); + } + enumerator->destroy(enumerator); + + send_response(this, request, RMC_DISCONNECT_ACK, client); + } + else + { + DBG1(DBG_CFG, "no IKE_SA matches %N, sending %N", + radius_message_code_names, RMC_DISCONNECT_REQUEST, + radius_message_code_names, RMC_DISCONNECT_NAK); + send_response(this, request, RMC_DISCONNECT_NAK, client); + } + ids->destroy_offset(ids, offsetof(ike_sa_id_t, destroy)); +} + +/** + * Apply a new lifetime to an IKE_SA + */ +static void apply_lifetime(private_eap_radius_dae_t *this, ike_sa_id_t *id, + u_int32_t lifetime) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); + if (ike_sa) + { + if (ike_sa->set_auth_lifetime(ike_sa, lifetime) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } +} + +/** + * Process a DAE CoA request, send response + */ +static void process_coa(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + linked_list_t *ids; + ike_sa_id_t *id; + chunk_t data; + int type; + u_int32_t lifetime = 0; + bool lifetime_seen = FALSE; + + ids = get_matching_ike_sas(this, request, client); + + if (ids->get_count(ids)) + { + enumerator = request->create_enumerator(request); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == RAT_SESSION_TIMEOUT && data.len == 4) + { + lifetime = untoh32(data.ptr); + lifetime_seen = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (lifetime_seen) + { + DBG1(DBG_CFG, "applying %us lifetime to %d IKE_SA%s matching %N, " + "sending %N", lifetime, ids->get_count(ids), + ids->get_count(ids) > 1 ? "s" : "", + radius_message_code_names, RMC_COA_REQUEST, + radius_message_code_names, RMC_COA_ACK); + + enumerator = ids->create_enumerator(ids); + while (enumerator->enumerate(enumerator, &id)) + { + apply_lifetime(this, id, lifetime); + } + enumerator->destroy(enumerator); + send_response(this, request, RMC_COA_ACK, client); + } + else + { + DBG1(DBG_CFG, "no Session-Timeout attribute found in %N, sending %N", + radius_message_code_names, RMC_COA_REQUEST, + radius_message_code_names, RMC_COA_NAK); + send_response(this, request, RMC_COA_NAK, client); + } + } + else + { + DBG1(DBG_CFG, "no IKE_SA matches %N, sending %N", + radius_message_code_names, RMC_COA_REQUEST, + radius_message_code_names, RMC_COA_NAK); + send_response(this, request, RMC_COA_NAK, client); + } + ids->destroy_offset(ids, offsetof(ike_sa_id_t, destroy)); +} + +/** + * Receive RADIUS DAE requests + */ +static job_requeue_t receive(private_eap_radius_dae_t *this) +{ + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + radius_message_t *request; + char buf[2048]; + ssize_t len; + bool oldstate; + host_t *client; + + oldstate = thread_cancelability(TRUE); + len = recvfrom(this->fd, buf, sizeof(buf), 0, + (struct sockaddr*)&addr, &addr_len); + thread_cancelability(oldstate); + + if (len > 0) + { + request = radius_message_parse(chunk_create(buf, len)); + if (request) + { + client = host_create_from_sockaddr((struct sockaddr*)&addr); + if (client) + { + if (!send_retransmit(this, request, client)) + { + if (request->verify(request, NULL, this->secret, + this->hasher, this->signer)) + { + switch (request->get_code(request)) + { + case RMC_DISCONNECT_REQUEST: + process_disconnect(this, request, client); + break; + case RMC_COA_REQUEST: + process_coa(this, request, client); + break; + default: + DBG1(DBG_CFG, "ignoring unsupported RADIUS DAE " + "%N message from %H", + radius_message_code_names, + request->get_code(request), client); + break; + } + } + } + client->destroy(client); + } + request->destroy(request); + } + else + { + DBG1(DBG_NET, "ignoring invalid RADIUS DAE request"); + } + } + else + { + DBG1(DBG_NET, "receving RADIUS DAE request failed: %s", strerror(errno)); + } + return JOB_REQUEUE_DIRECT; +} + +/** + * Open DAE socket + */ +static bool open_socket(private_eap_radius_dae_t *this) +{ + host_t *host; + + this->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->fd == -1) + { + DBG1(DBG_CFG, "unable to open RADIUS DAE socket: %s", strerror(errno)); + return FALSE; + } + + host = host_create_from_string( + lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.dae.listen", "0.0.0.0"), + lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.dae.port", RADIUS_DAE_PORT)); + if (!host) + { + DBG1(DBG_CFG, "invalid RADIUS DAE listen address"); + return FALSE; + } + + if (bind(this->fd, host->get_sockaddr(host), + *host->get_sockaddr_len(host)) == -1) + { + DBG1(DBG_CFG, "unable to bind RADIUS DAE socket: %s", strerror(errno)); + host->destroy(host); + return FALSE; + } + host->destroy(host); + return TRUE; +} + +METHOD(eap_radius_dae_t, destroy, void, + private_eap_radius_dae_t *this) +{ + if (this->job) + { + this->job->cancel(this->job); + } + if (this->fd != -1) + { + close(this->fd); + } + DESTROY_IF(this->signer); + DESTROY_IF(this->hasher); + this->responses->destroy_function(this->responses, (void*)entry_destroy); + free(this); +} + +/** + * See header + */ +eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting) +{ + private_eap_radius_dae_t *this; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .accounting = accounting, + .fd = -1, + .secret = { + .ptr = lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.dae.secret", NULL), + }, + .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5), + .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128), + .responses = linked_list_create(), + ); + + if (!this->hasher || !this->signer) + { + destroy(this); + return NULL; + } + if (!this->secret.ptr) + { + DBG1(DBG_CFG, "missing RADIUS DAE secret, disabled"); + destroy(this); + return NULL; + } + this->secret.len = strlen(this->secret.ptr); + this->signer->set_key(this->signer, this->secret); + + if (!open_socket(this)) + { + destroy(this); + return NULL; + } + + this->job = callback_job_create_with_prio((callback_job_cb_t)receive, + this, NULL, NULL, JOB_PRIO_CRITICAL); + lib->processor->queue_job(lib->processor, (job_t*)this->job); + + return &this->public; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_dae.h b/src/libcharon/plugins/eap_radius/eap_radius_dae.h new file mode 100644 index 000000000..759eadb49 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_dae.h @@ -0,0 +1,44 @@ +/* + * 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 eap_radius_dae eap_radius_dae + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_DAE_H_ +#define EAP_RADIUS_DAE_H_ + +#include "eap_radius_accounting.h" + +typedef struct eap_radius_dae_t eap_radius_dae_t; + +/** + * Dynamic Authorization Extensions (RFC 5176) for EAP-RADIUS. + */ +struct eap_radius_dae_t { + + /** + * Destroy a eap_radius_dae_t. + */ + void (*destroy)(eap_radius_dae_t *this); +}; + +/** + * Create a eap_radius_dae instance. + */ +eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting); + +#endif /** EAP_RADIUS_DAE_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_forward.c b/src/libcharon/plugins/eap_radius/eap_radius_forward.c new file mode 100644 index 000000000..16701bb57 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_forward.c @@ -0,0 +1,458 @@ +/* + * 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 "eap_radius_forward.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <utils/hashtable.h> +#include <threading/mutex.h> + +typedef struct private_eap_radius_forward_t private_eap_radius_forward_t; + +/** + * Private data of an eap_radius_forward_t object. + */ +struct private_eap_radius_forward_t { + + /** + * Public eap_radius_forward_t interface. + */ + eap_radius_forward_t public; + + /** + * List of attribute types to copy from IKE, as attr_t + */ + linked_list_t *from_attr; + + /** + * List of attribute types to copy to IKE, as attr_t + */ + linked_list_t *to_attr; + + /** + * Queued to forward from IKE, unique_id => linked_list_t of chunk_t + */ + hashtable_t *from; + + /** + * Queued to forward to IKE, unique_id => linked_list_t of chunk_t + */ + hashtable_t *to; + + /** + * Mutex to lock concurrent access to hashtables + */ + mutex_t *mutex; +}; + +/** + * RADIUS attribute selector + */ +typedef struct { + /** vendor ID, 0 for standard attributes */ + u_int32_t vendor; + /** attribute type */ + u_int8_t type; +} attr_t; + +/** + * Single instance of this + */ +static private_eap_radius_forward_t *singleton = NULL; + +/** + * Hashtable hash function + */ +static u_int hash(uintptr_t key) +{ + return key; +} + +/** + * Hashtable equals function + */ +static bool equals(uintptr_t a, uintptr_t b) +{ + return a == b; +} + +/** + * Free a queue entry + */ +static void free_attribute(chunk_t *chunk) +{ + free(chunk->ptr); + free(chunk); +} + +/** + * Lookup/create an attribute queue from a table + */ +static linked_list_t *lookup_queue(private_eap_radius_forward_t *this, + hashtable_t *table) +{ + linked_list_t *queue = NULL; + ike_sa_t *ike_sa; + uintptr_t id; + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN)) + { + id = ike_sa->get_unique_id(ike_sa); + this->mutex->lock(this->mutex); + queue = table->get(table, (void*)id); + if (!queue) + { + queue = linked_list_create(); + table->put(table, (void*)id, queue); + } + this->mutex->unlock(this->mutex); + } + return queue; +} + +/** + * Remove attribute queue from table + */ +static void remove_queue(private_eap_radius_forward_t *this, + hashtable_t *table, ike_sa_t *ike_sa) +{ + linked_list_t *queue; + + this->mutex->lock(this->mutex); + queue = table->remove(table, (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa)); + this->mutex->unlock(this->mutex); + if (queue) + { + queue->destroy_function(queue, (void*)free_attribute); + } +} + +/** + * Check if RADIUS attribute is contained in selector + */ +static bool is_attribute_selected(linked_list_t *selector, + radius_attribute_type_t type, chunk_t data) +{ + enumerator_t *enumerator; + u_int32_t vendor = 0; + attr_t *sel; + bool found = FALSE; + + if (type == RAT_VENDOR_SPECIFIC) + { + if (data.len < 4) + { + return FALSE; + } + vendor = untoh32(data.ptr); + } + enumerator = selector->create_enumerator(selector); + while (!found && enumerator->enumerate(enumerator, &sel)) + { + if (sel->vendor == vendor) + { + if (vendor) + { + if (sel->type == 0) + { /* any of that vendor is fine */ + found = TRUE; + } + else if (data.len > 4 && data.ptr[4] == sel->type) + { /* vendor specific type field, as defined in RFC 2865 */ + found = TRUE; + } + } + else + { + if (sel->type == type) + { + found = TRUE; + } + } + } + } + enumerator->destroy(enumerator); + + return found; +} + +/** + * Copy RADIUS attributes from queue to a RADIUS message + */ +static void queue2radius(linked_list_t *queue, radius_message_t *message) +{ + chunk_t *data; + + while (queue->remove_last(queue, (void**)&data) == SUCCESS) + { + if (data->len >= 2) + { + message->add(message, data->ptr[0], chunk_skip(*data, 2)); + } + free_attribute(data); + } +} + +/** + * Copy RADIUS attributes from a RADIUS message to the queue + */ +static void radius2queue(radius_message_t *message, linked_list_t *queue, + linked_list_t *selector) +{ + enumerator_t *enumerator; + int type; + chunk_t data, hdr, *ptr; + + enumerator = message->create_enumerator(message); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (is_attribute_selected(selector, type, data)) + { + hdr = chunk_alloc(2); + hdr.ptr[0] = type; + hdr.ptr[1] = data.len + 2; + + INIT(ptr); + *ptr = chunk_cat("mc", hdr, data); + queue->insert_last(queue, ptr); + } + } + enumerator->destroy(enumerator); +} + +/** + * Copy RADIUS attribute nofifies from IKE message to queue + */ +static void ike2queue(message_t *message, linked_list_t *queue, + linked_list_t *selector) +{ + enumerator_t *enumerator; + payload_t *payload; + notify_payload_t *notify; + chunk_t data, *ptr; + + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE) + { + data = notify->get_notification_data(notify); + if (data.len >= 2 && is_attribute_selected(selector, + data.ptr[0], chunk_skip(data, 2))) + { + INIT(ptr); + *ptr = chunk_clone(data); + queue->insert_last(queue, ptr); + } + } + } + } + enumerator->destroy(enumerator); +} + +/** + * Copy RADUIS attributes from queue to IKE message notifies + */ +static void queue2ike(linked_list_t *queue, message_t *message) +{ + chunk_t *data; + + while (queue->remove_last(queue, (void**)&data) == SUCCESS) + { + message->add_notify(message, FALSE, RADIUS_ATTRIBUTE, *data); + free_attribute(data); + } +} + +/** + * See header. + */ +void eap_radius_forward_from_ike(radius_message_t *request) +{ + private_eap_radius_forward_t *this = singleton; + linked_list_t *queue; + + if (this) + { + queue = lookup_queue(this, this->from); + if (queue) + { + queue2radius(queue, request); + } + } +} + +/** + * See header. + */ +void eap_radius_forward_to_ike(radius_message_t *response) +{ + private_eap_radius_forward_t *this = singleton; + linked_list_t *queue; + + if (this) + { + queue = lookup_queue(this, this->to); + if (queue) + { + radius2queue(response, queue, this->to_attr); + } + } +} + +METHOD(listener_t, message, bool, + private_eap_radius_forward_t *this, + ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain) +{ + linked_list_t *queue; + + if (plain && message->get_exchange_type(message) == IKE_AUTH) + { + if (incoming) + { + queue = lookup_queue(this, this->from); + if (queue) + { + ike2queue(message, queue, this->from_attr); + } + } + else + { + queue = lookup_queue(this, this->to); + if (queue) + { + queue2ike(queue, message); + } + } + } + return TRUE; +} + +METHOD(listener_t, ike_updown, bool, + private_eap_radius_forward_t *this, ike_sa_t *ike_sa, bool up) +{ + /* up or down, we don't need the state anymore */ + remove_queue(this, this->from, ike_sa); + remove_queue(this, this->to, ike_sa); + return TRUE; +} + +/** + * Parse a selector string to a list of attr_t selectors + */ +static linked_list_t* parse_selector(char *selector) +{ + enumerator_t *enumerator; + linked_list_t *list; + char *token, *pos; + + list = linked_list_create(); + enumerator = enumerator_create_token(selector, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + int type, vendor = 0; + attr_t *attr; + + pos = strchr(token, ':'); + if (pos) + { + *(pos++) = 0; + vendor = atoi(token); + token = pos; + } + type = enum_from_name(radius_attribute_type_names, token); + if (type == -1) + { + type = atoi(token); + } + if (vendor == 0 && type == 0) + { + DBG1(DBG_CFG, "ignoring unknown RADIUS attribute type '%s'", token); + } + else + { + INIT(attr, + .type = type, + .vendor = vendor, + ); + list->insert_last(list, attr); + if (!vendor) + { + DBG1(DBG_IKE, "forwarding RADIUS attribute %N", + radius_attribute_type_names, type); + } + else + { + DBG1(DBG_IKE, "forwarding RADIUS VSA %d-%d", vendor, type); + } + } + } + enumerator->destroy(enumerator); + return list; +} + +METHOD(eap_radius_forward_t, destroy, void, + private_eap_radius_forward_t *this) +{ + this->from_attr->destroy_function(this->from_attr, free); + this->to_attr->destroy_function(this->to_attr, free); + this->from->destroy(this->from); + this->to->destroy(this->to); + this->mutex->destroy(this->mutex); + free(this); + singleton = NULL; +} + +/** + * See header + */ +eap_radius_forward_t *eap_radius_forward_create() +{ + private_eap_radius_forward_t *this; + + INIT(this, + .public = { + .listener = { + .message = _message, + .ike_updown = _ike_updown, + }, + .destroy = _destroy, + }, + .from_attr = parse_selector(lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.forward.ike_to_radius", "")), + .to_attr = parse_selector(lib->settings->get_str(lib->settings, + "charon.plugins.eap-radius.forward.radius_to_ike", "")), + .from = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 8), + .to = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 8), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + if (this->from_attr->get_count(this->from_attr) == 0 && + this->to_attr->get_count(this->to_attr) == 0) + { + destroy(this); + return NULL; + } + + singleton = this; + return &this->public; +} diff --git a/src/libcharon/plugins/eap_radius/eap_radius_forward.h b/src/libcharon/plugins/eap_radius/eap_radius_forward.h new file mode 100644 index 000000000..2c1dbf7a8 --- /dev/null +++ b/src/libcharon/plugins/eap_radius/eap_radius_forward.h @@ -0,0 +1,65 @@ +/* + * 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 eap_radius_forward eap_radius_forward + * @{ @ingroup eap_radius + */ + +#ifndef EAP_RADIUS_FORWARD_H_ +#define EAP_RADIUS_FORWARD_H_ + +#include <radius_message.h> + +#include <bus/listeners/listener.h> + +typedef struct eap_radius_forward_t eap_radius_forward_t; + +/** + * Forward RADIUS attributes in Notifies between client and AAA backend. + */ +struct eap_radius_forward_t { + + /** + * Implements a listener. + */ + listener_t listener; + + /** + * Destroy a eap_radius_forward_t. + */ + void (*destroy)(eap_radius_forward_t *this); +}; + +/** + * Create a eap_radius_forward instance. + */ +eap_radius_forward_t *eap_radius_forward_create(); + +/** + * Forward RADIUS attributes from IKE notifies to a RADIUS request. + * + * @param request RADIUS request message to add attributes to + */ +void eap_radius_forward_from_ike(radius_message_t *request); + +/** + * Forward RADIUS attributes from a RADIUS response to IKE notifies. + * + * @param response RADIUS respose to read notifies from + */ +void eap_radius_forward_to_ike(radius_message_t *response); + +#endif /** EAP_RADIUS_FORWARD_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c index 4119ec571..8ee0ab81a 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c @@ -16,16 +16,25 @@ #include "eap_radius_plugin.h" #include "eap_radius.h" -#include "radius_client.h" -#include "radius_server.h" +#include "eap_radius_accounting.h" +#include "eap_radius_dae.h" +#include "eap_radius_forward.h" + +#include <radius_client.h> +#include <radius_config.h> #include <daemon.h> #include <threading/rwlock.h> /** - * Default RADIUS server port, when not configured + * Default RADIUS server port for authentication + */ +#define AUTH_PORT 1812 + +/** + * Default RADIUS server port for accounting */ -#define RADIUS_PORT 1812 +#define ACCT_PORT 1813 typedef struct private_eap_radius_plugin_t private_eap_radius_plugin_t; @@ -40,14 +49,29 @@ struct private_eap_radius_plugin_t { eap_radius_plugin_t public; /** - * List of RADIUS servers + * List of RADIUS server configurations */ - linked_list_t *servers; + linked_list_t *configs; /** - * Lock for server list + * Lock for configs list */ rwlock_t *lock; + + /** + * RADIUS sessions for accounting + */ + eap_radius_accounting_t *accounting; + + /** + * Dynamic authorization extensions + */ + eap_radius_dae_t *dae; + + /** + * RADIUS <-> IKE attribute forwarding + */ + eap_radius_forward_t *forward; }; /** @@ -58,12 +82,12 @@ static private_eap_radius_plugin_t *instance = NULL; /** * Load RADIUS servers from configuration */ -static void load_servers(private_eap_radius_plugin_t *this) +static void load_configs(private_eap_radius_plugin_t *this) { enumerator_t *enumerator; - radius_server_t *server; + radius_config_t *config; char *nas_identifier, *secret, *address, *section; - int port, sockets, preference; + int auth_port, acct_port, sockets, preference; address = lib->settings->get_str(lib->settings, "charon.plugins.eap-radius.server", NULL); @@ -78,18 +102,18 @@ static void load_servers(private_eap_radius_plugin_t *this) } nas_identifier = lib->settings->get_str(lib->settings, "charon.plugins.eap-radius.nas_identifier", "strongSwan"); - port = lib->settings->get_int(lib->settings, - "charon.plugins.eap-radius.port", RADIUS_PORT); + auth_port = lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.port", AUTH_PORT); sockets = lib->settings->get_int(lib->settings, "charon.plugins.eap-radius.sockets", 1); - server = radius_server_create(address, address, port, nas_identifier, - secret, sockets, 0); - if (!server) + config = radius_config_create(address, address, auth_port, ACCT_PORT, + nas_identifier, secret, sockets, 0); + if (!config) { DBG1(DBG_CFG, "no RADUIS server defined"); return; } - this->servers->insert_last(this->servers, server); + this->configs->insert_last(this->configs, config); return; } @@ -114,26 +138,32 @@ static void load_servers(private_eap_radius_plugin_t *this) nas_identifier = lib->settings->get_str(lib->settings, "charon.plugins.eap-radius.servers.%s.nas_identifier", "strongSwan", section); - port = lib->settings->get_int(lib->settings, - "charon.plugins.eap-radius.servers.%s.port", RADIUS_PORT, section); + auth_port = lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.servers.%s.auth_port", + lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.servers.%s.port", + AUTH_PORT, section), + section); + acct_port = lib->settings->get_int(lib->settings, + "charon.plugins.eap-radius.servers.%s.acct_port", ACCT_PORT, section); sockets = lib->settings->get_int(lib->settings, "charon.plugins.eap-radius.servers.%s.sockets", 1, section); preference = lib->settings->get_int(lib->settings, "charon.plugins.eap-radius.servers.%s.preference", 0, section); - server = radius_server_create(section, address, port, nas_identifier, - secret, sockets, preference); - if (!server) + config = radius_config_create(section, address, auth_port, acct_port, + nas_identifier, secret, sockets, preference); + if (!config) { DBG1(DBG_CFG, "loading RADIUS server '%s' failed, skipped", section); continue; } - this->servers->insert_last(this->servers, server); + this->configs->insert_last(this->configs, config); } enumerator->destroy(enumerator); DBG1(DBG_CFG, "loaded %d RADIUS server configuration%s", - this->servers->get_count(this->servers), - this->servers->get_count(this->servers) == 1 ? "" : "s"); + this->configs->get_count(this->configs), + this->configs->get_count(this->configs) == 1 ? "" : "s"); } METHOD(plugin_t, get_name, char*, @@ -160,10 +190,10 @@ METHOD(plugin_t, reload, bool, private_eap_radius_plugin_t *this) { this->lock->write_lock(this->lock); - this->servers->destroy_offset(this->servers, - offsetof(radius_server_t, destroy)); - this->servers = linked_list_create(); - load_servers(this); + this->configs->destroy_offset(this->configs, + offsetof(radius_config_t, destroy)); + this->configs = linked_list_create(); + load_configs(this); this->lock->unlock(this->lock); return TRUE; } @@ -171,9 +201,17 @@ METHOD(plugin_t, reload, bool, METHOD(plugin_t, destroy, void, private_eap_radius_plugin_t *this) { - this->servers->destroy_offset(this->servers, - offsetof(radius_server_t, destroy)); + if (this->forward) + { + charon->bus->remove_listener(charon->bus, &this->forward->listener); + this->forward->destroy(this->forward); + } + DESTROY_IF(this->dae); + this->configs->destroy_offset(this->configs, + offsetof(radius_config_t, destroy)); this->lock->destroy(this->lock); + charon->bus->remove_listener(charon->bus, &this->accounting->listener); + this->accounting->destroy(this->accounting); free(this); instance = NULL; } @@ -194,28 +232,73 @@ plugin_t *eap_radius_plugin_create() .destroy = _destroy, }, }, - .servers = linked_list_create(), + .configs = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .accounting = eap_radius_accounting_create(), + .forward = eap_radius_forward_create(), ); - load_servers(this); + load_configs(this); instance = this; + if (lib->settings->get_bool(lib->settings, + "charon.plugins.eap-radius.accounting", FALSE)) + { + charon->bus->add_listener(charon->bus, &this->accounting->listener); + } + if (lib->settings->get_bool(lib->settings, + "charon.plugins.eap-radius.dae.enable", FALSE)) + { + this->dae = eap_radius_dae_create(this->accounting); + } + if (this->forward) + { + charon->bus->add_listener(charon->bus, &this->forward->listener); + } + return &this->public.plugin; } /** * See header */ -enumerator_t *eap_radius_create_server_enumerator() +radius_client_t *eap_radius_create_client() { if (instance) { + enumerator_t *enumerator; + radius_config_t *config, *selected = NULL; + int current, best = -1; + instance->lock->read_lock(instance->lock); - return enumerator_create_cleaner( - instance->servers->create_enumerator(instance->servers), - (void*)instance->lock->unlock, instance->lock); + enumerator = instance->configs->create_enumerator(instance->configs); + while (enumerator->enumerate(enumerator, &config)) + { + current = config->get_preference(config); + if (current > best || + /* for two with equal preference, 50-50 chance */ + (current == best && random() % 2 == 0)) + { + DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d", + config->get_name(config), current); + best = current; + DESTROY_IF(selected); + selected = config->get_ref(config); + } + else + { + DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d", + config->get_name(config), current); + } + } + enumerator->destroy(enumerator); + instance->lock->unlock(instance->lock); + + if (selected) + { + return radius_client_create(selected); + } } - return enumerator_create_empty(); + return NULL; } diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.h b/src/libcharon/plugins/eap_radius/eap_radius_plugin.h index cb724364a..1570bd566 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.h +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.h @@ -25,7 +25,8 @@ #define EAP_RADIUS_PLUGIN_H_ #include <plugins/plugin.h> -#include <utils/enumerator.h> + +#include <radius_client.h> typedef struct eap_radius_plugin_t eap_radius_plugin_t; @@ -44,10 +45,10 @@ struct eap_radius_plugin_t { }; /** - * Create an enumerator over all loaded RADIUS servers. + * Get a RADIUS client instance to connect to servers. * - * @return enumerator over radius_server_t + * @return RADIUS client */ -enumerator_t *eap_radius_create_server_enumerator(); +radius_client_t *eap_radius_create_client(); #endif /** EAP_RADIUS_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/eap_tls/eap_tls.c b/src/libcharon/plugins/eap_tls/eap_tls.c index 39e1a60d9..dc0289ba2 100644 --- a/src/libcharon/plugins/eap_tls/eap_tls.c +++ b/src/libcharon/plugins/eap_tls/eap_tls.c @@ -39,7 +39,7 @@ struct private_eap_tls_t { }; /** Maximum number of EAP-TLS messages/fragments allowed */ -#define MAX_MESSAGE_COUNT 32 +#define MAX_MESSAGE_COUNT 32 /** Default size of a EAP-TLS fragment */ #define MAX_FRAGMENT_LEN 1024 @@ -148,8 +148,8 @@ static eap_tls_t *eap_tls_create(identification_t *server, max_msg_count = lib->settings->get_int(lib->settings, "charon.plugins.eap-tls.max_message_count", MAX_MESSAGE_COUNT); include_length = lib->settings->get_bool(lib->settings, - "charon.plugins.eap-tls.include_length", TRUE); - tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL); + "charon.plugins.eap-tls.include_length", TRUE); + tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TLS, NULL, NULL); this->tls_eap = tls_eap_create(EAP_TLS, tls, frag_size, max_msg_count, include_length); if (!this->tls_eap) diff --git a/src/libcharon/plugins/eap_ttls/Makefile.am b/src/libcharon/plugins/eap_ttls/Makefile.am index 94ce5cc1e..8cc82cc2e 100644 --- a/src/libcharon/plugins/eap_ttls/Makefile.am +++ b/src/libcharon/plugins/eap_ttls/Makefile.am @@ -1,6 +1,7 @@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \ - -I$(top_srcdir)/src/libcharon -I$(top_srcdir)/src/libtls + -I$(top_srcdir)/src/libcharon -I$(top_srcdir)/src/libtls \ + -I$(top_srcdir)/src/libradius AM_CFLAGS = -rdynamic diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls.c b/src/libcharon/plugins/eap_ttls/eap_ttls.c index 7193bc9f0..ace62f6b9 100644 --- a/src/libcharon/plugins/eap_ttls/eap_ttls.c +++ b/src/libcharon/plugins/eap_ttls/eap_ttls.c @@ -156,7 +156,8 @@ static eap_ttls_t *eap_ttls_create(identification_t *server, "charon.plugins.eap-ttls.max_message_count", MAX_MESSAGE_COUNT); include_length = lib->settings->get_bool(lib->settings, "charon.plugins.eap-ttls.include_length", TRUE); - tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TTLS, application); + tls = tls_create(is_server, server, peer, TLS_PURPOSE_EAP_TTLS, + application, NULL); this->tls_eap = tls_eap_create(EAP_TTLS, tls, frag_size, max_msg_count, include_length); if (!this->tls_eap) diff --git a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c index e75bd2976..767111b3e 100644 --- a/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c +++ b/src/libcharon/plugins/eap_ttls/eap_ttls_peer.c @@ -18,7 +18,7 @@ #include <debug.h> #include <daemon.h> - +#include <radius_message.h> #include <sa/eap/eap_method.h> typedef struct private_eap_ttls_peer_t private_eap_ttls_peer_t; @@ -64,8 +64,6 @@ struct private_eap_ttls_peer_t { eap_ttls_avp_t *avp; }; -#define MAX_RADIUS_ATTRIBUTE_SIZE 253 - METHOD(tls_application_t, process, status_t, private_eap_ttls_peer_t *this, bio_reader_t *reader) { diff --git a/src/libcharon/plugins/farp/farp_listener.c b/src/libcharon/plugins/farp/farp_listener.c index 30709c9eb..d1df4cc27 100644 --- a/src/libcharon/plugins/farp/farp_listener.c +++ b/src/libcharon/plugins/farp/farp_listener.c @@ -15,7 +15,7 @@ #include "farp_listener.h" -#include <utils/hashtable.h> +#include <utils/linked_list.h> #include <threading/rwlock.h> typedef struct private_farp_listener_t private_farp_listener_t; @@ -31,9 +31,9 @@ struct private_farp_listener_t { farp_listener_t public; /** - * Hashtable with active virtual IPs + * List with entry_t */ - hashtable_t *ips; + linked_list_t *entries; /** * RWlock for IP list @@ -42,88 +42,99 @@ struct private_farp_listener_t { }; /** - * Hashtable hash function + * Traffic selector cache entry */ -static u_int hash(host_t *key) +typedef struct { + /** list of local selectors */ + linked_list_t *local; + /** list of remote selectors */ + linked_list_t *remote; + /** reqid of CHILD_SA */ + u_int32_t reqid; +} entry_t; + +METHOD(listener_t, child_updown, bool, + private_farp_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, + bool up) { - return chunk_hash(key->get_address(key)); -} - -/** - * Hashtable equals function - */ -static bool equals(host_t *a, host_t *b) -{ - return a->ip_equals(a, b); -} + enumerator_t *enumerator; + entry_t *entry; -METHOD(listener_t, ike_updown, bool, - private_farp_listener_t *this, ike_sa_t *ike_sa, bool up) -{ - if (!up) + if (up) { - host_t *ip; - - ip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (ip) - { - this->lock->write_lock(this->lock); - ip = this->ips->remove(this->ips, ip); - this->lock->unlock(this->lock); - DESTROY_IF(ip); - } + INIT(entry, + .local = child_sa->get_traffic_selectors(child_sa, TRUE), + .remote = child_sa->get_traffic_selectors(child_sa, FALSE), + .reqid = child_sa->get_reqid(child_sa), + ); + entry->local = entry->local->clone_offset(entry->local, + offsetof(traffic_selector_t, clone)); + entry->remote = entry->remote->clone_offset(entry->remote, + offsetof(traffic_selector_t, clone)); + + this->lock->write_lock(this->lock); + this->entries->insert_last(this->entries, entry); + this->lock->unlock(this->lock); } - return TRUE; -} - -METHOD(listener_t, message_hook, bool, - private_farp_listener_t *this, ike_sa_t *ike_sa, - message_t *message, bool incoming, bool plain) -{ - if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && - message->get_exchange_type(message) == IKE_AUTH && - !message->get_request(message)) + else { - host_t *ip; - - ip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (ip) + this->lock->write_lock(this->lock); + enumerator = this->entries->create_enumerator(this->entries); + while (enumerator->enumerate(enumerator, &entry)) { - ip = ip->clone(ip); - this->lock->write_lock(this->lock); - ip = this->ips->put(this->ips, ip, ip); - this->lock->unlock(this->lock); - DESTROY_IF(ip); + if (entry->reqid == child_sa->get_reqid(child_sa)) + { + this->entries->remove_at(this->entries, enumerator); + entry->local->destroy_offset(entry->local, + offsetof(traffic_selector_t, destroy)); + entry->remote->destroy_offset(entry->remote, + offsetof(traffic_selector_t, destroy)); + free(entry); + } } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); } return TRUE; } -METHOD(farp_listener_t, is_active, bool, - private_farp_listener_t *this, host_t *ip) +METHOD(farp_listener_t, has_tunnel, bool, + private_farp_listener_t *this, host_t *local, host_t *remote) { - bool active; + enumerator_t *entries, *locals, *remotes; + traffic_selector_t *ts; + bool found = FALSE; + entry_t *entry; this->lock->read_lock(this->lock); - active = this->ips->get(this->ips, ip) != NULL; + entries = this->entries->create_enumerator(this->entries); + while (!found && entries->enumerate(entries, &entry)) + { + remotes = entry->remote->create_enumerator(entry->remote); + while (!found && remotes->enumerate(remotes, &ts)) + { + if (ts->includes(ts, remote)) + { + locals = entry->local->create_enumerator(entry->local); + while (!found && locals->enumerate(locals, &ts)) + { + found = ts->includes(ts, local); + } + locals->destroy(locals); + } + } + remotes->destroy(remotes); + } + entries->destroy(entries); this->lock->unlock(this->lock); - return active; + + return found; } METHOD(farp_listener_t, destroy, void, private_farp_listener_t *this) { - enumerator_t *enumerator; - host_t *key, *value; - - enumerator = this->ips->create_enumerator(this->ips); - while (enumerator->enumerate(enumerator, &key, &value)) - { - value->destroy(value); - } - enumerator->destroy(enumerator); - this->ips->destroy(this->ips); - + this->entries->destroy(this->entries); this->lock->destroy(this->lock); free(this); } @@ -138,14 +149,12 @@ farp_listener_t *farp_listener_create() INIT(this, .public = { .listener = { - .ike_updown = _ike_updown, - .message = _message_hook, + .child_updown = _child_updown, }, - .is_active = _is_active, + .has_tunnel = _has_tunnel, .destroy = _destroy, }, - .ips = hashtable_create((hashtable_hash_t)hash, - (hashtable_equals_t)equals, 8), + .entries = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); diff --git a/src/libcharon/plugins/farp/farp_listener.h b/src/libcharon/plugins/farp/farp_listener.h index bd96d7a1c..3155f60e2 100644 --- a/src/libcharon/plugins/farp/farp_listener.h +++ b/src/libcharon/plugins/farp/farp_listener.h @@ -37,12 +37,13 @@ struct farp_listener_t { listener_t listener; /** - * Check if a given IP is currently used as virtual IP by a peer. + * Check if we have a tunnel between two IP addresses. * - * @param ip IP to check - * @return TRUE if IP is an active virtual IP + * @param local local IP + * @param remote remote IP + * @return TRUE if a tunnel is active */ - bool (*is_active)(farp_listener_t *this, host_t *ip); + bool (*has_tunnel)(farp_listener_t *this, host_t *local, host_t *remote); /** * Destroy a farp_listener_t. diff --git a/src/libcharon/plugins/farp/farp_spoofer.c b/src/libcharon/plugins/farp/farp_spoofer.c index 7a8ca850b..587a3a74e 100644 --- a/src/libcharon/plugins/farp/farp_spoofer.c +++ b/src/libcharon/plugins/farp/farp_spoofer.c @@ -108,7 +108,7 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this) arp_t arp; int oldstate; ssize_t len; - host_t *ip; + host_t *local, *remote; oldstate = thread_cancelability(TRUE); len = recvfrom(this->skt, &arp, sizeof(arp), 0, @@ -117,16 +117,16 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this) if (len == sizeof(arp)) { - ip = host_create_from_chunk(AF_INET, + local = host_create_from_chunk(AF_INET, + chunk_create((char*)&arp.sender_ip, 4), 0); + remote = host_create_from_chunk(AF_INET, chunk_create((char*)&arp.target_ip, 4), 0); - if (ip) + if (this->listener->has_tunnel(this->listener, local, remote)) { - if (this->listener->is_active(this->listener, ip)) - { - send_arp(this, &arp, &addr); - } - ip->destroy(ip); + send_arp(this, &arp, &addr); } + local->destroy(local); + remote->destroy(remote); } return JOB_REQUEUE_DIRECT; diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 328b923b0..de5253b37 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -315,7 +315,7 @@ static void process_ike_update(private_ha_dispatcher_t *this, ike_sa_t *ike_sa = NULL; peer_cfg_t *peer_cfg = NULL; auth_cfg_t *auth; - bool received_vip = FALSE; + bool received_vip = FALSE, first_peer_addr = TRUE; enumerator = message->create_attribute_enumerator(message); while (enumerator->enumerate(enumerator, &attribute, &value)) @@ -355,9 +355,13 @@ static void process_ike_update(private_ha_dispatcher_t *this, ike_sa->set_virtual_ip(ike_sa, FALSE, value.host); received_vip = TRUE; break; - case HA_ADDITIONAL_ADDR: - ike_sa->add_additional_address(ike_sa, - value.host->clone(value.host)); + case HA_PEER_ADDR: + if (first_peer_addr) + { + ike_sa->clear_peer_addresses(ike_sa); + first_peer_addr = FALSE; + } + ike_sa->add_peer_address(ike_sa, value.host->clone(value.host)); break; case HA_CONFIG_NAME: peer_cfg = charon->backends->get_peer_cfg_by_name( diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index c8ad0f845..2819b9dd5 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -205,10 +205,10 @@ METHOD(listener_t, ike_updown, bool, m->add_attribute(m, HA_CONDITIONS, condition); m->add_attribute(m, HA_EXTENSIONS, extension); m->add_attribute(m, HA_CONFIG_NAME, peer_cfg->get_name(peer_cfg)); - enumerator = ike_sa->create_additional_address_enumerator(ike_sa); + enumerator = ike_sa->create_peer_address_enumerator(ike_sa); while (enumerator->enumerate(enumerator, (void**)&addr)) { - m->add_attribute(m, HA_ADDITIONAL_ADDR, addr); + m->add_attribute(m, HA_PEER_ADDR, addr); } enumerator->destroy(enumerator); } diff --git a/src/libcharon/plugins/ha/ha_kernel.c b/src/libcharon/plugins/ha/ha_kernel.c index 07a201557..2377a2630 100644 --- a/src/libcharon/plugins/ha/ha_kernel.c +++ b/src/libcharon/plugins/ha/ha_kernel.c @@ -274,11 +274,14 @@ METHOD(ha_kernel_t, activate, void, char *file; enumerator = enumerator_create_directory(CLUSTERIP_DIR); - while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + if (enumerator) { - enable_disable(this, segment, file, TRUE); + while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + { + enable_disable(this, segment, file, TRUE); + } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } METHOD(ha_kernel_t, deactivate, void, @@ -288,11 +291,14 @@ METHOD(ha_kernel_t, deactivate, void, char *file; enumerator = enumerator_create_directory(CLUSTERIP_DIR); - while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + if (enumerator) { - enable_disable(this, segment, file, FALSE); + while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + { + enable_disable(this, segment, file, FALSE); + } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } /** @@ -306,23 +312,26 @@ static void disable_all(private_ha_kernel_t *this) int i; enumerator = enumerator_create_directory(CLUSTERIP_DIR); - while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + if (enumerator) { - if (chown(file, charon->uid, charon->gid) != 0) + while (enumerator->enumerate(enumerator, NULL, &file, NULL)) { - DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", - strerror(errno)); - } - active = get_active(this, file); - for (i = 1; i <= this->count; i++) - { - if (active & SEGMENTS_BIT(i)) + if (chown(file, charon->uid, charon->gid) != 0) + { + DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", + strerror(errno)); + } + active = get_active(this, file); + for (i = 1; i <= this->count; i++) { - enable_disable(this, i, file, FALSE); + if (active & SEGMENTS_BIT(i)) + { + enable_disable(this, i, file, FALSE); + } } } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } METHOD(ha_kernel_t, destroy, void, diff --git a/src/libcharon/plugins/ha/ha_message.c b/src/libcharon/plugins/ha/ha_message.c index 52317e532..6b00ed83f 100644 --- a/src/libcharon/plugins/ha/ha_message.c +++ b/src/libcharon/plugins/ha/ha_message.c @@ -187,7 +187,7 @@ METHOD(ha_message_t, add_attribute, void, case HA_REMOTE_ADDR: case HA_LOCAL_VIP: case HA_REMOTE_VIP: - case HA_ADDITIONAL_ADDR: + case HA_PEER_ADDR: { host_encoding_t *enc; host_t *host; @@ -395,7 +395,7 @@ METHOD(enumerator_t, attribute_enumerate, bool, case HA_REMOTE_ADDR: case HA_LOCAL_VIP: case HA_REMOTE_VIP: - case HA_ADDITIONAL_ADDR: + case HA_PEER_ADDR: { host_encoding_t *enc; diff --git a/src/libcharon/plugins/ha/ha_message.h b/src/libcharon/plugins/ha/ha_message.h index 22a5bd46a..8cd30f711 100644 --- a/src/libcharon/plugins/ha/ha_message.h +++ b/src/libcharon/plugins/ha/ha_message.h @@ -100,8 +100,8 @@ enum ha_message_attribute_t { HA_LOCAL_VIP, /** host_t*, remote virtual IP */ HA_REMOTE_VIP, - /** host_t*, additional MOBIKE peer address */ - HA_ADDITIONAL_ADDR, + /** host_t*, known peer addresses (used for MOBIKE) */ + HA_PEER_ADDR, /** u_int8_t, initiator of an exchange, TRUE for local */ HA_INITIATOR, /** chunk_t, initiators nonce */ diff --git a/src/libcharon/plugins/ha/ha_segments.h b/src/libcharon/plugins/ha/ha_segments.h index eb9e5c1d5..76da38082 100644 --- a/src/libcharon/plugins/ha/ha_segments.h +++ b/src/libcharon/plugins/ha/ha_segments.h @@ -55,7 +55,7 @@ struct ha_segments_t { * Activate a set of IKE_SAs identified by a segment. * * @param segment numerical segment to takeover, 0 for all - * @param notify wheter to notify other nodes about activation + * @param notify whether to notify other nodes about activation */ void (*activate)(ha_segments_t *this, u_int segment, bool notify); @@ -63,7 +63,7 @@ struct ha_segments_t { * Deactivate a set of IKE_SAs identified by a segment. * * @param segment numerical segment to takeover, 0 for all - * @param notify wheter to notify other nodes about deactivation + * @param notify whether to notify other nodes about deactivation */ void (*deactivate)(ha_segments_t *this, u_int segment, bool notify); diff --git a/src/libcharon/plugins/radattr/Makefile.am b/src/libcharon/plugins/radattr/Makefile.am new file mode 100644 index 000000000..0ea8df5d1 --- /dev/null +++ b/src/libcharon/plugins/radattr/Makefile.am @@ -0,0 +1,17 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon -I$(top_srcdir)/src/libradius + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-radattr.la +else +libstrongswan_radattr_la_LIBADD = $(top_builddir)/src/libradius/libradius.la +plugin_LTLIBRARIES = libstrongswan-radattr.la +endif + +libstrongswan_radattr_la_SOURCES = radattr_plugin.h radattr_plugin.c \ + radattr_listener.h radattr_listener.c + +libstrongswan_radattr_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/radattr/radattr_listener.c b/src/libcharon/plugins/radattr/radattr_listener.c new file mode 100644 index 000000000..88ab60582 --- /dev/null +++ b/src/libcharon/plugins/radattr/radattr_listener.c @@ -0,0 +1,221 @@ +/* + * 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 "radattr_listener.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <errno.h> + +#include <daemon.h> + +#include <radius_message.h> + +/** + * Maximum size of an attribute to add + */ +#define MAX_ATTR_SIZE 1024 + +typedef struct private_radattr_listener_t private_radattr_listener_t; + +/** + * Private data of an radattr_listener_t object. + */ +struct private_radattr_listener_t { + + /** + * Public radattr_listener_t interface. + */ + radattr_listener_t public; + + /** + * Directory to look for attribute files + */ + char *dir; + + /** + * IKE_AUTH message ID to attribute + */ + int mid; +}; + +/** + * Print RADIUS attributes found in IKE message notifies + */ +static void print_radius_attributes(private_radattr_listener_t *this, + message_t *message) +{ + radius_attribute_type_t type; + enumerator_t *enumerator; + notify_payload_t *notify; + payload_t *payload; + chunk_t data; + + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE) + { + data = notify->get_notification_data(notify); + if (data.len >= 2) + { + type = data.ptr[0]; + data = chunk_skip(data, 2); + if (chunk_printable(data, NULL, 0)) + { + DBG1(DBG_IKE, "received RADIUS %N: %.*s", + radius_attribute_type_names, type, + (int)data.len, data.ptr); + } + else + { + DBG1(DBG_IKE, "received RADIUS %N: %#B", + radius_attribute_type_names, type, &data); + + } + } + } + } + } + enumerator->destroy(enumerator); +} + +/** + * Add a RADIUS attribute from a client-ID specific file to an IKE message + */ +static void add_radius_attribute(private_radattr_listener_t *this, + ike_sa_t *ike_sa, message_t *message) +{ + if (this->dir && + (this->mid == -1 || message->get_message_id(message) == this->mid)) + { + identification_t *id; + auth_cfg_t *auth; + char path[PATH_MAX]; + chunk_t data; + struct stat sb; + void *addr; + int fd; + + auth = ike_sa->get_auth_cfg(ike_sa, TRUE); + id = auth->get(auth, AUTH_RULE_EAP_IDENTITY); + if (!id) + { + id = ike_sa->get_my_id(ike_sa); + } + + snprintf(path, sizeof(path), "%s/%Y", this->dir, id); + fd = open(path, O_RDONLY); + if (fd != -1) + { + if (fstat(fd, &sb) != -1) + { + if (sb.st_size <= MAX_ATTR_SIZE) + { + addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr != MAP_FAILED) + { + data = chunk_create(addr, sb.st_size); + if (data.len >= 2) + { + DBG1(DBG_CFG, "adding RADIUS %N attribute", + radius_attribute_type_names, data.ptr[0]); + message->add_notify(message, FALSE, + RADIUS_ATTRIBUTE, data); + } + munmap(addr, sb.st_size); + } + else + { + DBG1(DBG_CFG, "mapping RADIUS attribute '%s' failed: %s", + path, strerror(errno)); + } + } + else + { + DBG1(DBG_CFG, "RADIUS attribute '%s' exceeds size limit", + path); + } + } + else + { + DBG1(DBG_CFG, "fstat RADIUS attribute '%s' failed: %s", + path, strerror(errno)); + } + close(fd); + } + else + { + DBG1(DBG_CFG, "reading RADIUS attribute '%s' failed: %s", + path, strerror(errno)); + } + } +} + +METHOD(listener_t, message, bool, + private_radattr_listener_t *this, + ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain) +{ + if (plain && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN) && + message->get_exchange_type(message) == IKE_AUTH && + message->get_payload(message, EXTENSIBLE_AUTHENTICATION)) + { + if (incoming) + { + print_radius_attributes(this, message); + } + else + { + add_radius_attribute(this, ike_sa, message); + } + } + return TRUE; +} + + +METHOD(radattr_listener_t, destroy, void, + private_radattr_listener_t *this) +{ + free(this); +} + +/** + * See header + */ +radattr_listener_t *radattr_listener_create() +{ + private_radattr_listener_t *this; + + INIT(this, + .public = { + .listener = { + .message = _message, + }, + .destroy = _destroy, + }, + .dir = lib->settings->get_str(lib->settings, + "charon.plugins.radattr.dir", NULL), + .mid = lib->settings->get_int(lib->settings, + "charon.plugins.radattr.message_id", -1), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/radattr/radattr_listener.h b/src/libcharon/plugins/radattr/radattr_listener.h new file mode 100644 index 000000000..9a14827fc --- /dev/null +++ b/src/libcharon/plugins/radattr/radattr_listener.h @@ -0,0 +1,49 @@ +/* + * 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 radattr_listener radattr_listener + * @{ @ingroup + */ + +#ifndef RADATTR_LISTENER_H_ +#define RADATTR_LISTENER_H_ + +#include <bus/listeners/listener.h> + +typedef struct radattr_listener_t radattr_listener_t; + +/** + * Output received RADIUS attributes, inject custom attributes. + */ +struct radattr_listener_t { + + /** + * Implements a listener. + */ + listener_t listener; + + /** + * Destroy a radattr_listener_t. + */ + void (*destroy)(radattr_listener_t *this); +}; + +/** + * Create a radattr_listener instance. + */ +radattr_listener_t *radattr_listener_create(); + +#endif /** RADATTR_LISTENER_H_ @}*/ diff --git a/src/libcharon/plugins/radattr/radattr_plugin.c b/src/libcharon/plugins/radattr/radattr_plugin.c new file mode 100644 index 000000000..85ea326ac --- /dev/null +++ b/src/libcharon/plugins/radattr/radattr_plugin.c @@ -0,0 +1,75 @@ +/* + * 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 "radattr_plugin.h" + +#include "radattr_listener.h" + +#include <daemon.h> + +typedef struct private_radattr_plugin_t private_radattr_plugin_t; + +/** + * private data of radattr plugin + */ +struct private_radattr_plugin_t { + + /** + * implements plugin interface + */ + radattr_plugin_t public; + + /** + * Listener acting on messages + */ + radattr_listener_t *listener; +}; + +METHOD(plugin_t, get_name, char*, + private_radattr_plugin_t *this) +{ + return "radattr"; +} + +METHOD(plugin_t, destroy, void, + private_radattr_plugin_t *this) +{ + charon->bus->remove_listener(charon->bus, &this->listener->listener); + this->listener->destroy(this->listener); + free(this); +} + +/** + * Plugin constructor + */ +plugin_t *radattr_plugin_create() +{ + private_radattr_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .reload = (void*)return_false, + .destroy = _destroy, + }, + }, + .listener = radattr_listener_create(), + ); + + charon->bus->add_listener(charon->bus, &this->listener->listener); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/radattr/radattr_plugin.h b/src/libcharon/plugins/radattr/radattr_plugin.h new file mode 100644 index 000000000..c3bad5a3a --- /dev/null +++ b/src/libcharon/plugins/radattr/radattr_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup radattr radattr + * @ingroup cplugins + * + * @defgroup radattr_plugin radattr_plugin + * @{ @ingroup radattr + */ + +#ifndef RADATTR_PLUGIN_H_ +#define RADATTR_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct radattr_plugin_t radattr_plugin_t; + +/** + * Plugin to inject/process custom RADIUS attributes. + */ +struct radattr_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** RADATTR_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index 3264cb802..a58d904c5 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -609,8 +609,7 @@ METHOD(stroke_control_t, unroute, void, { child_sa_t *child_sa; enumerator_t *enumerator; - u_int32_t id; - bool found = FALSE; + u_int32_t id = 0; if (charon->shunts->uninstall(charon->shunts, msg->unroute.name)) { @@ -624,15 +623,17 @@ METHOD(stroke_control_t, unroute, void, if (streq(msg->unroute.name, child_sa->get_name(child_sa))) { id = child_sa->get_reqid(child_sa); - enumerator->destroy(enumerator); - charon->traps->uninstall(charon->traps, id); - fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name); - found = TRUE; + break; } } enumerator->destroy(enumerator); - if (!found) + if (id) + { + charon->traps->uninstall(charon->traps, id); + fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name); + } + else { fprintf(out, "configuration '%s' not found\n", msg->unroute.name); } diff --git a/src/libcharon/plugins/stroke/stroke_cred.c b/src/libcharon/plugins/stroke/stroke_cred.c index aff0e66b1..cdf69135a 100644 --- a/src/libcharon/plugins/stroke/stroke_cred.c +++ b/src/libcharon/plugins/stroke/stroke_cred.c @@ -71,6 +71,12 @@ struct private_stroke_cred_t { mem_cred_t *creds; /** + * ignore missing CA basic constraint (i.e. treat all certificates in + * ipsec.conf ca sections and ipsec.d/cacert as CA certificates) + */ + bool force_ca_cert; + + /** * cache CRLs to disk? */ bool cachecrl; @@ -91,10 +97,21 @@ METHOD(stroke_cred_t, load_ca, certificate_t*, snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename); } - cert = lib->creds->create(lib->creds, + if (this->force_ca_cert) + { /* we treat this certificate as a CA certificate even if it has no + * CA basic constraint */ + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, path, BUILD_X509_FLAG, X509_CA, + BUILD_END); + } + else + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, path, BUILD_END); + } if (cert) { x509_t *x509 = (x509_t*)cert; @@ -171,11 +188,21 @@ static void load_certdir(private_stroke_cred_t *this, char *path, { case CERT_X509: if (flag & X509_CA) - { /* for CA certificates, we strictly require - * the CA basic constraint to be set */ - cert = lib->creds->create(lib->creds, + { + if (this->force_ca_cert) + { /* treat this certificate as CA cert even it has no + * CA basic constraint */ + cert = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, BUILD_X509_FLAG, + X509_CA, BUILD_END); + } + else + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, file, BUILD_END); + } if (cert) { x509_t *x509 = (x509_t*)cert; @@ -1073,6 +1100,9 @@ stroke_cred_t *stroke_cred_create() lib->credmgr->add_set(lib->credmgr, &this->creds->set); + this->force_ca_cert = lib->settings->get_bool(lib->settings, + "charon.plugins.stroke.ignore_missing_ca_basic_constraint", FALSE); + load_certs(this); load_secrets(this, SECRETS_FILE, 0, NULL); diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c index 8bb1a98ef..6b256f29b 100644 --- a/src/libcharon/plugins/stroke/stroke_list.c +++ b/src/libcharon/plugins/stroke/stroke_list.c @@ -426,7 +426,6 @@ METHOD(stroke_list_t, status, void, if (all) { peer_cfg_t *peer_cfg; - plugin_t *plugin; char *pool; host_t *host; u_int32_t dpd; @@ -461,14 +460,8 @@ METHOD(stroke_list_t, status, void, } fprintf(out, ", scheduled: %d\n", lib->scheduler->get_job_load(lib->scheduler)); - fprintf(out, " loaded plugins: "); - enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); - while (enumerator->enumerate(enumerator, &plugin, NULL)) - { - fprintf(out, "%s ", plugin->get_name(plugin)); - } - enumerator->destroy(enumerator); - fprintf(out, "\n"); + fprintf(out, " loaded plugins: %s\n", + lib->plugins->loaded_plugins(lib->plugins)); first = TRUE; enumerator = this->attribute->create_pool_enumerator(this->attribute); diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 21d15afe6..4956b011f 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -25,7 +26,10 @@ #include <hydra.h> #include <daemon.h> +#include <threading/mutex.h> #include <threading/thread.h> +#include <threading/condvar.h> +#include <utils/linked_list.h> #include <processing/jobs/callback_job.h> #include "stroke_config.h" @@ -35,6 +39,12 @@ #include "stroke_attribute.h" #include "stroke_list.h" +/** + * To avoid clogging the thread pool with (blocking) jobs, we limit the number + * of concurrently handled stroke commands. + */ +#define MAX_CONCURRENT_DEFAULT 4 + typedef struct stroke_job_context_t stroke_job_context_t; typedef struct private_stroke_socket_t private_stroke_socket_t; @@ -56,7 +66,37 @@ struct private_stroke_socket_t { /** * job accepting stroke messages */ - callback_job_t *job; + callback_job_t *receiver; + + /** + * job handling stroke messages + */ + callback_job_t *handler; + + /** + * queued stroke commands + */ + linked_list_t *commands; + + /** + * lock for command list + */ + mutex_t *mutex; + + /** + * condvar to signal the arrival or completion of commands + */ + condvar_t *condvar; + + /** + * the number of currently handled commands + */ + u_int handling; + + /** + * the maximum number of concurrently handled commands + */ + u_int max_concurrent; /** * configuration backend @@ -84,7 +124,7 @@ struct private_stroke_socket_t { stroke_ca_t *ca; /** - * Status information logging + * status information logging */ stroke_list_t *list; }; @@ -450,7 +490,7 @@ static void stroke_loglevel(private_stroke_socket_t *this, msg->loglevel.level, msg->loglevel.type); group = enum_from_name(debug_names, msg->loglevel.type); - if (group < 0) + if ((int)group < 0) { fprintf(out, "invalid type (%s)!\n", msg->loglevel.type); return; @@ -492,6 +532,18 @@ static void stroke_job_context_destroy(stroke_job_context_t *this) } /** + * called to signal the completion of a command + */ +static inline job_requeue_t job_processed(private_stroke_socket_t *this) +{ + this->mutex->lock(this->mutex); + this->handling--; + this->condvar->signal(this->condvar); + this->mutex->unlock(this->mutex); + return JOB_REQUEUE_NONE; +} + +/** * process a stroke request from the socket pointed by "fd" */ static job_requeue_t process(stroke_job_context_t *ctx) @@ -509,7 +561,7 @@ static job_requeue_t process(stroke_job_context_t *ctx) { DBG1(DBG_CFG, "reading length of stroke message failed: %s", strerror(errno)); - return JOB_REQUEUE_NONE; + return job_processed(this); } /* read message */ @@ -518,14 +570,14 @@ static job_requeue_t process(stroke_job_context_t *ctx) if (bytes_read != msg_length) { DBG1(DBG_CFG, "reading stroke message failed: %s", strerror(errno)); - return JOB_REQUEUE_NONE; + return job_processed(this); } out = fdopen(strokefd, "w+"); if (out == NULL) { DBG1(DBG_CFG, "opening stroke output channel failed: %s", strerror(errno)); - return JOB_REQUEUE_NONE; + return job_processed(this); } DBG3(DBG_CFG, "stroke message %b", (void*)msg, msg_length); @@ -602,11 +654,38 @@ static job_requeue_t process(stroke_job_context_t *ctx) fclose(out); /* fclose() closes underlying FD */ ctx->fd = 0; - return JOB_REQUEUE_NONE; + return job_processed(this); } /** - * Implementation of private_stroke_socket_t.stroke_receive. + * Handle queued stroke commands + */ +static job_requeue_t handle(private_stroke_socket_t *this) +{ + stroke_job_context_t *ctx; + callback_job_t *job; + bool oldstate; + + this->mutex->lock(this->mutex); + thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex); + oldstate = thread_cancelability(TRUE); + while (this->commands->get_count(this->commands) == 0 || + this->handling >= this->max_concurrent) + { + this->condvar->wait(this->condvar, this->mutex); + } + thread_cancelability(oldstate); + this->commands->remove_first(this->commands, (void**)&ctx); + this->handling++; + thread_cleanup_pop(TRUE); + job = callback_job_create_with_prio((callback_job_cb_t)process, ctx, + (void*)stroke_job_context_destroy, this->handler, JOB_PRIO_HIGH); + lib->processor->queue_job(lib->processor, (job_t*)job); + return JOB_REQUEUE_DIRECT; +} + +/** + * Accept stroke commands and queue them to be handled */ static job_requeue_t receive(private_stroke_socket_t *this) { @@ -614,7 +693,6 @@ static job_requeue_t receive(private_stroke_socket_t *this) int strokeaddrlen = sizeof(strokeaddr); int strokefd; bool oldstate; - callback_job_t *job; stroke_job_context_t *ctx; oldstate = thread_cancelability(TRUE); @@ -627,17 +705,18 @@ static job_requeue_t receive(private_stroke_socket_t *this) return JOB_REQUEUE_FAIR; } - ctx = malloc_thing(stroke_job_context_t); - ctx->fd = strokefd; - ctx->this = this; - job = callback_job_create_with_prio((callback_job_cb_t)process, - ctx, (void*)stroke_job_context_destroy, this->job, JOB_PRIO_HIGH); - lib->processor->queue_job(lib->processor, (job_t*)job); + INIT(ctx, + .fd = strokefd, + .this = this, + ); + this->mutex->lock(this->mutex); + this->commands->insert_last(this->commands, ctx); + this->condvar->signal(this->condvar); + this->mutex->unlock(this->mutex); return JOB_REQUEUE_FAIR; } - /** * initialize and open stroke socket */ @@ -685,7 +764,11 @@ static bool open_socket(private_stroke_socket_t *this) METHOD(stroke_socket_t, destroy, void, private_stroke_socket_t *this) { - this->job->cancel(this->job); + this->handler->cancel(this->handler); + this->receiver->cancel(this->receiver); + this->commands->destroy_function(this->commands, (void*)stroke_job_context_destroy); + this->condvar->destroy(this->condvar); + this->mutex->destroy(this->mutex); lib->credmgr->remove_set(lib->credmgr, &this->ca->set); lib->credmgr->remove_set(lib->credmgr, &this->cred->set); charon->backends->remove_backend(charon->backends, &this->config->backend); @@ -725,14 +808,24 @@ stroke_socket_t *stroke_socket_create() this->control = stroke_control_create(); this->list = stroke_list_create(this->attribute); + this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); + this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + this->commands = linked_list_create(); + this->max_concurrent = lib->settings->get_int(lib->settings, + "charon.plugins.stroke.max_concurrent", MAX_CONCURRENT_DEFAULT); + lib->credmgr->add_set(lib->credmgr, &this->ca->set); lib->credmgr->add_set(lib->credmgr, &this->cred->set); charon->backends->add_backend(charon->backends, &this->config->backend); hydra->attributes->add_provider(hydra->attributes, &this->attribute->provider); - this->job = callback_job_create_with_prio((callback_job_cb_t)receive, + this->receiver = callback_job_create_with_prio((callback_job_cb_t)receive, + this, NULL, NULL, JOB_PRIO_CRITICAL); + lib->processor->queue_job(lib->processor, (job_t*)this->receiver); + + this->handler = callback_job_create_with_prio((callback_job_cb_t)handle, this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); + lib->processor->queue_job(lib->processor, (job_t*)this->handler); return &this->public; } diff --git a/src/libcharon/plugins/tnc_imc/tnc_imc.c b/src/libcharon/plugins/tnc_imc/tnc_imc.c index 52e526604..a1f2d770f 100644 --- a/src/libcharon/plugins/tnc_imc/tnc_imc.c +++ b/src/libcharon/plugins/tnc_imc/tnc_imc.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Mike McCauley - * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil + * Copyright (C) 2010-2011 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 @@ -17,8 +18,11 @@ #include <dlfcn.h> +#include <tncif_pa_subtypes.h> + #include <debug.h> #include <library.h> +#include <utils/linked_list.h> #include <threading/mutex.h> typedef struct private_tnc_imc_t private_tnc_imc_t; @@ -54,9 +58,19 @@ struct private_tnc_imc_t { TNC_IMCID id; /** - * List of message types supported by IMC + * list of additional IMC IDs */ - TNC_MessageTypeList supported_types; + linked_list_t *additional_ids; + + /** + * List of message types supported by IMC - Vendor ID part + */ + TNC_VendorIDList supported_vids; + + /** + * List of message types supported by IMC - Subtype part + */ + TNC_MessageSubtypeList supported_subtypes; /** * Number of supported message types @@ -81,6 +95,54 @@ METHOD(imc_t, get_id, TNC_IMCID, return this->id; } +METHOD(imc_t, add_id, void, + private_tnc_imc_t *this, TNC_IMCID id) +{ + void *pointer; + + /* store the scalar value in the pointer */ + pointer = (void*)id; + this->additional_ids->insert_last(this->additional_ids, pointer); +} + +METHOD(imc_t, has_id, bool, + private_tnc_imc_t *this, TNC_IMCID id) +{ + enumerator_t *enumerator; + TNC_IMCID additional_id; + void *pointer; + bool found = FALSE; + + /* check primary IMC ID */ + if (id == this->id) + { + return TRUE; + } + + /* return if there are no additional IMC IDs */ + if (this->additional_ids->get_count(this->additional_ids) == 0) + { + return FALSE; + } + + /* check additional IMC IDs */ + enumerator = this->additional_ids->create_enumerator(this->additional_ids); + while (enumerator->enumerate(enumerator, &pointer)) + { + /* interpret pointer as scalar value */ + additional_id = (TNC_UInt32)pointer; + + if (id == additional_id) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + METHOD(imc_t, get_name, char*, private_tnc_imc_t *this) { @@ -91,66 +153,150 @@ METHOD(imc_t, set_message_types, void, private_tnc_imc_t *this, TNC_MessageTypeList supported_types, TNC_UInt32 type_count) { - char buf[512]; + char buf[BUF_LEN]; char *pos = buf; int len = sizeof(buf); - int written; + int i, written; + size_t size; + TNC_VendorID vid; + TNC_MessageSubtype subtype; + enum_name_t *pa_subtype_names; /* lock the imc_t instance */ this->mutex->lock(this->mutex); - /* Free an existing MessageType list */ - free(this->supported_types); - this->supported_types = NULL; + /* Free existing VendorID and MessageSubtype lists */ + free(this->supported_vids); + this->supported_vids = NULL; + free(this->supported_subtypes); + this->supported_subtypes = NULL; /* Store the new MessageType list */ this->type_count = type_count; if (type_count && supported_types) { - size_t size = type_count * sizeof(TNC_MessageType); - int i; + size = type_count * sizeof(TNC_VendorID); + this->supported_vids = malloc(size); + size = type_count * sizeof(TNC_MessageSubtype); + this->supported_subtypes = malloc(size); for (i = 0; i < type_count; i++) { - written = snprintf(pos, len, " 0x%08x", supported_types[i]); + vid = (supported_types[i] >> 8) & TNC_VENDORID_ANY; + subtype = supported_types[i] & TNC_SUBTYPE_ANY; + + pa_subtype_names = get_pa_subtype_names(vid); + if (pa_subtype_names) + { + written = snprintf(pos, len," '%N/%N' 0x%06x/0x%02x", + pen_names, vid, pa_subtype_names, subtype, + vid, subtype); + } + else + { + written = snprintf(pos, len," '%N' 0x%06x/0x%02x", + pen_names, vid, vid, subtype); + } if (written >= len) { break; } pos += written; len -= written; + + this->supported_vids[i] = vid; + this->supported_subtypes[i] = subtype; } - this->supported_types = malloc(size); - memcpy(this->supported_types, supported_types, size); } *pos = '\0'; DBG2(DBG_TNC, "IMC %u supports %u message type%s:%s", this->id, type_count, (type_count == 1) ? "":"s", buf); + /* unlock the imc_t instance */ + this->mutex->unlock(this->mutex); +} + +METHOD(imc_t, set_message_types_long, void, + private_tnc_imc_t *this, TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, TNC_UInt32 type_count) +{ + char buf[BUF_LEN]; + char *pos = buf; + int len = sizeof(buf); + int i, written; + size_t size; + TNC_VendorID vid; + TNC_MessageSubtype subtype; + enum_name_t *pa_subtype_names; + /* lock the imc_t instance */ + this->mutex->lock(this->mutex); + + /* Free existing VendorID and MessageSubtype lists */ + free(this->supported_vids); + this->supported_vids = NULL; + free(this->supported_subtypes); + this->supported_subtypes = NULL; + + /* Store the new MessageType list */ + this->type_count = type_count; + if (type_count && supported_vids && supported_subtypes) + { + size = type_count * sizeof(TNC_VendorID); + this->supported_vids = malloc(size); + memcpy(this->supported_vids, supported_vids, size); + size = type_count * sizeof(TNC_MessageSubtype); + this->supported_subtypes = malloc(size); + memcpy(this->supported_subtypes, supported_subtypes, size); + + for (i = 0; i < type_count; i++) + { + vid = supported_vids[i]; + subtype = supported_subtypes[i]; + + pa_subtype_names = get_pa_subtype_names(vid); + if (pa_subtype_names) + { + written = snprintf(pos, len," '%N/%N' 0x%06x/0x%08x", + pen_names, vid, pa_subtype_names, subtype, + vid, subtype); + } + else + { + written = snprintf(pos, len," '%N' 0x%06x/0x%08x", + pen_names, vid, vid, subtype); + } + if (written >= len) + { + break; + } + pos += written; + len -= written; + } + } + *pos = '\0'; + DBG2(DBG_TNC, "IMC %u supports %u message type%s:%s", + this->id, type_count, (type_count == 1) ? "":"s", buf); + + /* unlock the imc_t instance */ this->mutex->unlock(this->mutex); } METHOD(imc_t, type_supported, bool, - private_tnc_imc_t *this, TNC_MessageType message_type) + private_tnc_imc_t *this, TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype) { - TNC_VendorID msg_vid, vid; - TNC_MessageSubtype msg_subtype, subtype; + TNC_VendorID vid; + TNC_MessageSubtype subtype; int i; - msg_vid = (message_type >> 8) & TNC_VENDORID_ANY; - msg_subtype = message_type & TNC_SUBTYPE_ANY; - for (i = 0; i < this->type_count; i++) { - vid = (this->supported_types[i] >> 8) & TNC_VENDORID_ANY; - subtype = this->supported_types[i] & TNC_SUBTYPE_ANY; - - if (this->supported_types[i] == message_type - || (subtype == TNC_SUBTYPE_ANY - && (msg_vid == vid || vid == TNC_VENDORID_ANY)) - || (vid == TNC_VENDORID_ANY - && (msg_subtype == subtype || subtype == TNC_SUBTYPE_ANY))) + vid = this->supported_vids[i]; + subtype = this->supported_subtypes[i]; + + if ((vid == TNC_VENDORID_ANY && subtype == TNC_SUBTYPE_ANY) || + (vid == msg_vid && (subtype == TNC_SUBTYPE_ANY || + subtype == msg_subtype))) { return TRUE; } @@ -163,7 +309,9 @@ METHOD(imc_t, destroy, void, { dlclose(this->handle); this->mutex->destroy(this->mutex); - free(this->supported_types); + this->additional_ids->destroy(this->additional_ids); + free(this->supported_vids); + free(this->supported_subtypes); free(this->name); free(this->path); free(this); @@ -180,13 +328,17 @@ imc_t* tnc_imc_create(char *name, char *path) .public = { .set_id = _set_id, .get_id = _get_id, + .add_id = _add_id, + .has_id = _has_id, .get_name = _get_name, .set_message_types = _set_message_types, + .set_message_types_long = _set_message_types_long, .type_supported = _type_supported, .destroy = _destroy, }, .name = name, .path = path, + .additional_ids = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); @@ -220,6 +372,8 @@ imc_t* tnc_imc_create(char *name, char *path) } this->public.receive_message = dlsym(this->handle, "TNC_IMC_ReceiveMessage"); + this->public.receive_message_long = + dlsym(this->handle, "TNC_IMC_ReceiveMessageLong"); this->public.batch_ending = dlsym(this->handle, "TNC_IMC_BatchEnding"); this->public.terminate = diff --git a/src/libcharon/plugins/tnc_imc/tnc_imc_bind_function.c b/src/libcharon/plugins/tnc_imc/tnc_imc_bind_function.c index 46c131b44..90a607ccc 100644 --- a/src/libcharon/plugins/tnc_imc/tnc_imc_bind_function.c +++ b/src/libcharon/plugins/tnc_imc/tnc_imc_bind_function.c @@ -19,8 +19,6 @@ #include <debug.h> -#define TNC_IMVID_ANY 0xffff - /** * Called by the IMC to inform a TNCC about the set of message types the IMC * is able to receive @@ -40,6 +38,25 @@ TNC_Result TNC_TNCC_ReportMessageTypes(TNC_IMCID imc_id, } /** + * Called by the IMC to inform a TNCC about the set of message types the IMC + * is able to receive. This function supports long message types. + */ +TNC_Result TNC_TNCC_ReportMessageTypesLong(TNC_IMCID imc_id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count) +{ + if (!tnc->imcs->is_registered(tnc->imcs, imc_id)) + { + DBG1(DBG_TNC, "ignoring ReportMessageTypesLong() from unregistered IMC %u", + imc_id); + return TNC_RESULT_INVALID_PARAMETER; + } + return tnc->imcs->set_message_types_long(tnc->imcs, imc_id, supported_vids, + supported_subtypes, type_count); +} + +/** * Called by the IMC to ask a TNCC to retry an Integrity Check Handshake */ TNC_Result TNC_TNCC_RequestHandshakeRetry(TNC_IMCID imc_id, @@ -65,14 +82,97 @@ TNC_Result TNC_TNCC_SendMessage(TNC_IMCID imc_id, TNC_UInt32 msg_len, TNC_MessageType msg_type) { + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + if (!tnc->imcs->is_registered(tnc->imcs, imc_id)) { DBG1(DBG_TNC, "ignoring SendMessage() from unregistered IMC %u", imc_id); return TNC_RESULT_INVALID_PARAMETER; } + msg_vid = (msg_type >> 8) & TNC_VENDORID_ANY; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + return tnc->tnccs->send_message(tnc->tnccs, imc_id, TNC_IMVID_ANY, - connection_id, msg, msg_len, msg_type); + connection_id, 0, msg, msg_len, msg_vid, msg_subtype); +} + +/** + * Called by the IMC when an IMC-IMV message is to be sent over IF-TNCCS 2.0 + */ +TNC_Result TNC_TNCC_SendMessageLong(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 imv_id) +{ + if (!tnc->imcs->is_registered(tnc->imcs, imc_id)) + { + DBG1(DBG_TNC, "ignoring SendMessage() from unregistered IMC %u", + imc_id); + return TNC_RESULT_INVALID_PARAMETER; + } + return tnc->tnccs->send_message(tnc->tnccs, imc_id, imv_id, connection_id, + msg_flags, msg, msg_len, msg_vid, msg_subtype); +} + +/** + * Called by the IMC to get the value of an attribute associated with a + * connection or with the TNCC as a whole. + */ +TNC_Result TNC_TNCC_GetAttribute(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_AttributeID attribute_id, + TNC_UInt32 buffer_len, + TNC_BufferReference buffer, + TNC_UInt32 *out_value_len) +{ + if (!tnc->imcs->is_registered(tnc->imcs, imc_id)) + { + DBG1(DBG_TNC, "ignoring GetAttribute() from unregistered IMC %u", + imc_id); + return TNC_RESULT_INVALID_PARAMETER; + } + return tnc->tnccs->get_attribute(tnc->tnccs, TRUE, imc_id, connection_id, + attribute_id, buffer_len, buffer, out_value_len); +} + +/** + * Called by the IMC to set the value of an attribute associated with a + * connection or with the TNCC as a whole. + */ +TNC_Result TNC_TNCC_SetAttribute(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_AttributeID attribute_id, + TNC_UInt32 buffer_len, + TNC_BufferReference buffer) +{ + if (!tnc->imcs->is_registered(tnc->imcs, imc_id)) + { + DBG1(DBG_TNC, "ignoring SetAttribute() from unregistered IMC %u", + imc_id); + return TNC_RESULT_INVALID_PARAMETER; + } + return tnc->tnccs->set_attribute(tnc->tnccs, TRUE, imc_id, connection_id, + attribute_id, buffer_len, buffer); +} + +/** + * Called by the IMC when it wants to reserve an additional IMC ID for itself + */ +TNC_Result TNC_TNCC_ReserveAdditionalIMCID(TNC_IMCID imc_id, TNC_UInt32 *new_id) +{ + if (tnc->imcs->reserve_id(tnc->imcs, imc_id, new_id)) + { + return TNC_RESULT_SUCCESS; + } + DBG1(DBG_TNC, "ignoring ReserveAdditionalIMCID() from unregistered IMC %u", + imc_id); + return TNC_RESULT_INVALID_PARAMETER; } /** @@ -86,6 +186,10 @@ TNC_Result TNC_TNCC_BindFunction(TNC_IMCID id, { *function_pointer = (void*)TNC_TNCC_ReportMessageTypes; } + else if (streq(function_name, "TNC_TNCC_ReportMessageTypesLong")) + { + *function_pointer = (void*)TNC_TNCC_ReportMessageTypesLong; + } else if (streq(function_name, "TNC_TNCC_RequestHandshakeRetry")) { *function_pointer = (void*)TNC_TNCC_RequestHandshakeRetry; @@ -94,6 +198,22 @@ TNC_Result TNC_TNCC_BindFunction(TNC_IMCID id, { *function_pointer = (void*)TNC_TNCC_SendMessage; } + else if (streq(function_name, "TNC_TNCC_SendMessageLong")) + { + *function_pointer = (void*)TNC_TNCC_SendMessageLong; + } + else if (streq(function_name, "TNC_TNCC_GetAttribute")) + { + *function_pointer = (void*)TNC_TNCC_GetAttribute; + } + else if (streq(function_name, "TNC_TNCC_SetAttribute")) + { + *function_pointer = (void*)TNC_TNCC_SetAttribute; + } + else if (streq(function_name, "TNC_TNCC_ReserveAdditionalIMCID")) + { + *function_pointer = (void*)TNC_TNCC_ReserveAdditionalIMCID; + } else { return TNC_RESULT_INVALID_PARAMETER; diff --git a/src/libcharon/plugins/tnc_imc/tnc_imc_manager.c b/src/libcharon/plugins/tnc_imc/tnc_imc_manager.c index 202df5f5c..e101cf974 100644 --- a/src/libcharon/plugins/tnc_imc/tnc_imc_manager.c +++ b/src/libcharon/plugins/tnc_imc/tnc_imc_manager.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Mike McCauley - * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil + * Copyright (C) 2010-2011 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 @@ -130,9 +131,34 @@ METHOD(imc_manager_t, is_registered, bool, enumerator = this->imcs->create_enumerator(this->imcs); while (enumerator->enumerate(enumerator, &imc)) { - if (id == imc->get_id(imc)) + if (imc->has_id(imc, id)) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + +METHOD(imc_manager_t, reserve_id, bool, + private_tnc_imc_manager_t *this, TNC_IMCID id, TNC_UInt32 *new_id) +{ + enumerator_t *enumerator; + imc_t *imc; + bool found = FALSE; + + enumerator = this->imcs->create_enumerator(this->imcs); + while (enumerator->enumerate(enumerator, &imc)) + { + if (imc->get_id(imc)) { found = TRUE; + *new_id = this->next_imc_id++; + imc->add_id(imc, *new_id); + DBG2(DBG_TNC, "additional ID %u reserved for IMC with primary ID %u", + *new_id, id); break; } } @@ -203,30 +229,77 @@ METHOD(imc_manager_t, set_message_types, TNC_Result, return result; } +METHOD(imc_manager_t, set_message_types_long, TNC_Result, + private_tnc_imc_manager_t *this, TNC_IMCID id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count) +{ + enumerator_t *enumerator; + imc_t *imc; + TNC_Result result = TNC_RESULT_FATAL; + + enumerator = this->imcs->create_enumerator(this->imcs); + while (enumerator->enumerate(enumerator, &imc)) + { + if (id == imc->get_id(imc)) + { + imc->set_message_types_long(imc, supported_vids, supported_subtypes, + type_count); + result = TNC_RESULT_SUCCESS; + break; + } + } + enumerator->destroy(enumerator); + return result; +} + METHOD(imc_manager_t, receive_message, void, private_tnc_imc_manager_t *this, TNC_ConnectionID connection_id, - TNC_BufferReference message, - TNC_UInt32 message_len, - TNC_MessageType message_type) + bool excl, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) { bool type_supported = FALSE; + TNC_MessageType msg_type; + TNC_UInt32 msg_flags; enumerator_t *enumerator; imc_t *imc; enumerator = this->imcs->create_enumerator(this->imcs); while (enumerator->enumerate(enumerator, &imc)) { - if (imc->receive_message && imc->type_supported(imc, message_type)) + if (imc->type_supported(imc, msg_vid, msg_subtype) && + (!excl || (excl && imc->has_id(imc, dst_imc_id)) )) { - type_supported = TRUE; - imc->receive_message(imc->get_id(imc), connection_id, - message, message_len, message_type); + if (imc->receive_message_long && src_imv_id) + { + type_supported = TRUE; + msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0; + imc->receive_message_long(imc->get_id(imc), connection_id, + msg_flags, msg, msg_len, msg_vid, msg_subtype, + src_imv_id, dst_imc_id); + + } + else if (imc->receive_message && msg_vid <= TNC_VENDORID_ANY && + msg_subtype <= TNC_SUBTYPE_ANY) + { + type_supported = TRUE; + msg_type = (msg_vid << 8) | msg_subtype; + imc->receive_message(imc->get_id(imc), connection_id, + msg, msg_len, msg_type); + } } } enumerator->destroy(enumerator); if (!type_supported) { - DBG2(DBG_TNC, "message type 0x%08x not supported by any IMC", message_type); + DBG2(DBG_TNC, "message type 0x%06x/0x%08x not supported by any IMC", + msg_vid, msg_subtype); } } @@ -279,10 +352,12 @@ imc_manager_t* tnc_imc_manager_create(void) .remove = _remove_, /* avoid name conflict with stdio.h */ .load = _load, .is_registered = _is_registered, + .reserve_id = _reserve_id, .get_preferred_language = _get_preferred_language, .notify_connection_change = _notify_connection_change, .begin_handshake = _begin_handshake, .set_message_types = _set_message_types, + .set_message_types_long = _set_message_types_long, .receive_message = _receive_message, .batch_ending = _batch_ending, .destroy = _destroy, diff --git a/src/libcharon/plugins/tnc_imv/tnc_imv.c b/src/libcharon/plugins/tnc_imv/tnc_imv.c index f9cfc3417..f0b150743 100644 --- a/src/libcharon/plugins/tnc_imv/tnc_imv.c +++ b/src/libcharon/plugins/tnc_imv/tnc_imv.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Mike McCauley - * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil + * Copyright (C) 2010-2011 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 @@ -17,8 +18,11 @@ #include <dlfcn.h> +#include <tncif_pa_subtypes.h> + #include <debug.h> #include <library.h> +#include <utils/linked_list.h> #include <threading/mutex.h> typedef struct private_tnc_imv_t private_tnc_imv_t; @@ -54,9 +58,19 @@ struct private_tnc_imv_t { TNC_IMVID id; /** - * List of message types supported by IMC + * List of additional IMV IDs */ - TNC_MessageTypeList supported_types; + linked_list_t *additional_ids; + + /** + * List of message types supported by IMV - Vendor ID part + */ + TNC_VendorIDList supported_vids; + + /** + * List of message types supported by IMV - Subtype part + */ + TNC_MessageSubtypeList supported_subtypes; /** * Number of supported message types @@ -81,6 +95,50 @@ METHOD(imv_t, get_id, TNC_IMVID, return this->id; } +METHOD(imv_t, add_id, void, + private_tnc_imv_t *this, TNC_IMVID id) +{ + TNC_IMVID *new_id; + + new_id = malloc_thing(TNC_IMVID); + *new_id = id; + this->additional_ids->insert_last(this->additional_ids, new_id); +} + +METHOD(imv_t, has_id, bool, + private_tnc_imv_t *this, TNC_IMVID id) +{ + enumerator_t *enumerator; + TNC_IMVID *additional_id; + bool found = FALSE; + + /* check primary IMV ID */ + if (id == this->id) + { + return TRUE; + } + + /* return if there are no additional IMV IDs */ + if (this->additional_ids->get_count(this->additional_ids) == 0) + { + return FALSE; + } + + /* check additional IMV IDs */ + enumerator = this->additional_ids->create_enumerator(this->additional_ids); + while (enumerator->enumerate(enumerator, &additional_id)) + { + if (id == *additional_id) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + METHOD(imv_t, get_name, char*, private_tnc_imv_t *this) { @@ -91,67 +149,150 @@ METHOD(imv_t, set_message_types, void, private_tnc_imv_t *this, TNC_MessageTypeList supported_types, TNC_UInt32 type_count) { - char buf[512]; + char buf[BUF_LEN]; char *pos = buf; int len = sizeof(buf); - int written; + int i, written; + size_t size; + TNC_VendorID vid; + TNC_MessageSubtype subtype; + enum_name_t *pa_subtype_names; /* lock the imv_t instance */ this->mutex->lock(this->mutex); - /* Free an existing MessageType list */ - free(this->supported_types); - this->supported_types = NULL; + /* Free existing VendorID and MessageSubtype lists */ + free(this->supported_vids); + this->supported_vids = NULL; + free(this->supported_subtypes); + this->supported_subtypes = NULL; /* Store the new MessageType list */ this->type_count = type_count; if (type_count && supported_types) { - size_t size = type_count * sizeof(TNC_MessageType); - - int i; + size = type_count * sizeof(TNC_VendorID); + this->supported_vids = malloc(size); + size = type_count * sizeof(TNC_MessageSubtype); + this->supported_subtypes = malloc(size); for (i = 0; i < type_count; i++) { - written = snprintf(pos, len, " 0x%08x", supported_types[i]); + vid = (supported_types[i] >> 8) & TNC_VENDORID_ANY; + subtype = supported_types[i] & TNC_SUBTYPE_ANY; + + pa_subtype_names = get_pa_subtype_names(vid); + if (pa_subtype_names) + { + written = snprintf(pos, len," '%N/%N' 0x%06x/0x%02x", + pen_names, vid, pa_subtype_names, subtype, + vid, subtype); + } + else + { + written = snprintf(pos, len," '%N' 0x%06x/0x%02x", + pen_names, vid, vid, subtype); + } if (written >= len) { break; } pos += written; len -= written; + + this->supported_vids[i] = vid; + this->supported_subtypes[i] = subtype; } - this->supported_types = malloc(size); - memcpy(this->supported_types, supported_types, size); } *pos = '\0'; DBG2(DBG_TNC, "IMV %u supports %u message type%s:%s", this->id, type_count, (type_count == 1) ? "":"s", buf); + /* unlock the imv_t instance */ + this->mutex->unlock(this->mutex); +} + +METHOD(imv_t, set_message_types_long, void, + private_tnc_imv_t *this, TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, TNC_UInt32 type_count) +{ + char buf[BUF_LEN]; + char *pos = buf; + int len = sizeof(buf); + int i, written; + size_t size; + TNC_VendorID vid; + TNC_MessageSubtype subtype; + enum_name_t *pa_subtype_names; + /* lock the imv_t instance */ + this->mutex->lock(this->mutex); + + /* Free existing VendorID and MessageSubtype lists */ + free(this->supported_vids); + this->supported_vids = NULL; + free(this->supported_subtypes); + this->supported_subtypes = NULL; + + /* Store the new MessageType list */ + this->type_count = type_count; + if (type_count && supported_vids && supported_subtypes) + { + size = type_count * sizeof(TNC_VendorID); + this->supported_vids = malloc(size); + memcpy(this->supported_vids, supported_vids, size); + size = type_count * sizeof(TNC_MessageSubtype); + this->supported_subtypes = malloc(size); + memcpy(this->supported_subtypes, supported_subtypes, size); + + for (i = 0; i < type_count; i++) + { + vid = supported_vids[i]; + subtype = supported_subtypes[i]; + + pa_subtype_names = get_pa_subtype_names(vid); + if (pa_subtype_names) + { + written = snprintf(pos, len," '%N/%N' 0x%06x/0x%08x", + pen_names, vid, pa_subtype_names, subtype, + vid, subtype); + } + else + { + written = snprintf(pos, len," '%N' 0x%06x/0x%08x", + pen_names, vid, vid, subtype); + } + if (written >= len) + { + break; + } + pos += written; + len -= written; + } + } + *pos = '\0'; + DBG2(DBG_TNC, "IMV %u supports %u message type%s:%s", + this->id, type_count, (type_count == 1) ? "":"s", buf); + + /* unlock the imv_t instance */ this->mutex->unlock(this->mutex); } METHOD(imv_t, type_supported, bool, - private_tnc_imv_t *this, TNC_MessageType message_type) + private_tnc_imv_t *this, TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype) { - TNC_VendorID msg_vid, vid; - TNC_MessageSubtype msg_subtype, subtype; + TNC_VendorID vid; + TNC_MessageSubtype subtype; int i; - msg_vid = (message_type >> 8) & TNC_VENDORID_ANY; - msg_subtype = message_type & TNC_SUBTYPE_ANY; - for (i = 0; i < this->type_count; i++) { - vid = (this->supported_types[i] >> 8) & TNC_VENDORID_ANY; - subtype = this->supported_types[i] & TNC_SUBTYPE_ANY; - - if (this->supported_types[i] == message_type - || (subtype == TNC_SUBTYPE_ANY - && (msg_vid == vid || vid == TNC_VENDORID_ANY)) - || (vid == TNC_VENDORID_ANY - && (msg_subtype == subtype || subtype == TNC_SUBTYPE_ANY))) + vid = this->supported_vids[i]; + subtype = this->supported_subtypes[i]; + + if ((vid == TNC_VENDORID_ANY && subtype == TNC_SUBTYPE_ANY) || + (vid == msg_vid && (subtype == TNC_SUBTYPE_ANY || + subtype == msg_subtype))) { return TRUE; } @@ -164,7 +305,9 @@ METHOD(imv_t, destroy, void, { dlclose(this->handle); this->mutex->destroy(this->mutex); - free(this->supported_types); + this->additional_ids->destroy_function(this->additional_ids, free); + free(this->supported_vids); + free(this->supported_subtypes); free(this->name); free(this->path); free(this); @@ -181,13 +324,17 @@ imv_t* tnc_imv_create(char *name, char *path) .public = { .set_id = _set_id, .get_id = _get_id, + .add_id = _add_id, + .has_id = _has_id, .get_name = _get_name, .set_message_types = _set_message_types, + .set_message_types_long = _set_message_types_long, .type_supported = _type_supported, .destroy = _destroy, }, .name = name, .path = path, + .additional_ids = linked_list_create(), .mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); @@ -222,6 +369,8 @@ imv_t* tnc_imv_create(char *name, char *path) } this->public.receive_message = dlsym(this->handle, "TNC_IMV_ReceiveMessage"); + this->public.receive_message_long = + dlsym(this->handle, "TNC_IMV_ReceiveMessageLong"); this->public.batch_ending = dlsym(this->handle, "TNC_IMV_BatchEnding"); this->public.terminate = diff --git a/src/libcharon/plugins/tnc_imv/tnc_imv_bind_function.c b/src/libcharon/plugins/tnc_imv/tnc_imv_bind_function.c index cde1e4fe1..dd11c5009 100644 --- a/src/libcharon/plugins/tnc_imv/tnc_imv_bind_function.c +++ b/src/libcharon/plugins/tnc_imv/tnc_imv_bind_function.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Mike McCauley - * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil + * Copyright (C) 2010-2011 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 @@ -19,8 +20,6 @@ #include <debug.h> -#define TNC_IMCID_ANY 0xffff - /** * Called by the IMV to inform a TNCS about the set of message types the IMV * is able to receive @@ -40,6 +39,25 @@ TNC_Result TNC_TNCS_ReportMessageTypes(TNC_IMVID imv_id, } /** + * Called by the IMV to inform a TNCS about the set of message types the IMV + * is able to receive. This function supports long message types. + */ +TNC_Result TNC_TNCS_ReportMessageTypesLong(TNC_IMVID imv_id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count) +{ + if (!tnc->imvs->is_registered(tnc->imvs, imv_id)) + { + DBG1(DBG_TNC, "ignoring ReportMessageTypesLong() from unregistered IMV %u", + imv_id); + return TNC_RESULT_INVALID_PARAMETER; + } + return tnc->imvs->set_message_types_long(tnc->imvs, imv_id, supported_vids, + supported_subtypes, type_count); +} + +/** * Called by the IMV to ask a TNCS to retry an Integrity Check Handshake */ TNC_Result TNC_TNCS_RequestHandshakeRetry(TNC_IMVID imv_id, @@ -65,14 +83,42 @@ TNC_Result TNC_TNCS_SendMessage(TNC_IMVID imv_id, TNC_UInt32 msg_len, TNC_MessageType msg_type) { + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + if (!tnc->imvs->is_registered(tnc->imvs, imv_id)) { DBG1(DBG_TNC, "ignoring SendMessage() from unregistered IMV %u", imv_id); return TNC_RESULT_INVALID_PARAMETER; } + msg_vid = (msg_type >> 8) & TNC_VENDORID_ANY; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + return tnc->tnccs->send_message(tnc->tnccs, TNC_IMCID_ANY, imv_id, - connection_id, msg, msg_len, msg_type); + connection_id, 0, msg, msg_len, msg_vid, msg_subtype); +} + +/** + * Called by the IMV when an IMV-IMC message is to be sent over IF-TNCCS 2.0 + */ +TNC_Result TNC_TNCS_SendMessageLong(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 imc_id) +{ + if (!tnc->imvs->is_registered(tnc->imvs, imv_id)) + { + DBG1(DBG_TNC, "ignoring SendMessageLong() from unregistered IMV %u", + imv_id); + return TNC_RESULT_INVALID_PARAMETER; + } + return tnc->tnccs->send_message(tnc->tnccs, imc_id, imv_id, connection_id, + msg_flags, msg, msg_len, msg_vid, msg_subtype); } /** @@ -111,7 +157,7 @@ TNC_Result TNC_TNCS_GetAttribute(TNC_IMVID imv_id, imv_id); return TNC_RESULT_INVALID_PARAMETER; } - return tnc->tnccs->get_attribute(tnc->tnccs, imv_id, connection_id, + return tnc->tnccs->get_attribute(tnc->tnccs, FALSE, imv_id, connection_id, attribute_id, buffer_len, buffer, out_value_len); } @@ -131,11 +177,25 @@ TNC_Result TNC_TNCS_SetAttribute(TNC_IMVID imv_id, imv_id); return TNC_RESULT_INVALID_PARAMETER; } - return tnc->tnccs->set_attribute(tnc->tnccs, imv_id, connection_id, + return tnc->tnccs->set_attribute(tnc->tnccs, FALSE, imv_id, connection_id, attribute_id, buffer_len, buffer); } /** + * Called by the IMV when it wants to reserve an additional IMV ID for itself + */ +TNC_Result TNC_TNCS_ReserveAdditionalIMVID(TNC_IMVID imv_id, TNC_UInt32 *new_id) +{ + if (tnc->imvs->reserve_id(tnc->imvs, imv_id, new_id)) + { + return TNC_RESULT_SUCCESS; + } + DBG1(DBG_TNC, "ignoring ReserveAdditionalIMVID() from unregistered IMV %u", + imv_id); + return TNC_RESULT_INVALID_PARAMETER; +} + +/** * Called by the IMV when it needs a function pointer */ TNC_Result TNC_TNCS_BindFunction(TNC_IMVID id, @@ -146,6 +206,10 @@ TNC_Result TNC_TNCS_BindFunction(TNC_IMVID id, { *function_pointer = (void*)TNC_TNCS_ReportMessageTypes; } + else if (streq(function_name, "TNC_TNCS_ReportMessageTypesLong")) + { + *function_pointer = (void*)TNC_TNCS_ReportMessageTypesLong; + } else if (streq(function_name, "TNC_TNCS_RequestHandshakeRetry")) { *function_pointer = (void*)TNC_TNCS_RequestHandshakeRetry; @@ -154,6 +218,10 @@ TNC_Result TNC_TNCS_BindFunction(TNC_IMVID id, { *function_pointer = (void*)TNC_TNCS_SendMessage; } + else if (streq(function_name, "TNC_TNCS_SendMessageLong")) + { + *function_pointer = (void*)TNC_TNCS_SendMessageLong; + } else if (streq(function_name, "TNC_TNCS_ProvideRecommendation")) { *function_pointer = (void*)TNC_TNCS_ProvideRecommendation; @@ -166,6 +234,10 @@ TNC_Result TNC_TNCS_BindFunction(TNC_IMVID id, { *function_pointer = (void*)TNC_TNCS_SetAttribute; } + else if (streq(function_name, "TNC_TNCS_ReserveAdditionalIMVID")) + { + *function_pointer = (void*)TNC_TNCS_ReserveAdditionalIMVID; + } else { return TNC_RESULT_INVALID_PARAMETER; diff --git a/src/libcharon/plugins/tnc_imv/tnc_imv_manager.c b/src/libcharon/plugins/tnc_imv/tnc_imv_manager.c index 4eee69e4d..b1da73156 100644 --- a/src/libcharon/plugins/tnc_imv/tnc_imv_manager.c +++ b/src/libcharon/plugins/tnc_imv/tnc_imv_manager.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Mike McCauley - * Copyright (C) 2010 Andreas Steffen, HSR Hochschule fuer Technik Rapperswil + * Copyright (C) 2010-2011 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 @@ -147,9 +148,34 @@ METHOD(imv_manager_t, is_registered, bool, enumerator = this->imvs->create_enumerator(this->imvs); while (enumerator->enumerate(enumerator, &imv)) { - if (id == imv->get_id(imv)) + if (imv->has_id(imv, id)) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + +METHOD(imv_manager_t, reserve_id, bool, + private_tnc_imv_manager_t *this, TNC_IMVID id, TNC_UInt32 *new_id) +{ + enumerator_t *enumerator; + imv_t *imv; + bool found = FALSE; + + enumerator = this->imvs->create_enumerator(this->imvs); + while (enumerator->enumerate(enumerator, &imv)) + { + if (imv->get_id(imv)) { found = TRUE; + *new_id = this->next_imv_id++; + imv->add_id(imv, *new_id); + DBG2(DBG_TNC, "additional ID %u reserved for IMV with primary ID %u", + *new_id, id); break; } } @@ -267,6 +293,31 @@ METHOD(imv_manager_t, set_message_types, TNC_Result, return result; } +METHOD(imv_manager_t, set_message_types_long, TNC_Result, + private_tnc_imv_manager_t *this, TNC_IMVID id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count) +{ + enumerator_t *enumerator; + imv_t *imv; + TNC_Result result = TNC_RESULT_FATAL; + + enumerator = this->imvs->create_enumerator(this->imvs); + while (enumerator->enumerate(enumerator, &imv)) + { + if (id == imv->get_id(imv)) + { + imv->set_message_types_long(imv, supported_vids, supported_subtypes, + type_count); + result = TNC_RESULT_SUCCESS; + break; + } + } + enumerator->destroy(enumerator); + return result; +} + METHOD(imv_manager_t, solicit_recommendation, void, private_tnc_imv_manager_t *this, TNC_ConnectionID id) { @@ -283,28 +334,52 @@ METHOD(imv_manager_t, solicit_recommendation, void, METHOD(imv_manager_t, receive_message, void, private_tnc_imv_manager_t *this, TNC_ConnectionID connection_id, - TNC_BufferReference message, - TNC_UInt32 message_len, - TNC_MessageType message_type) + bool excl, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) { bool type_supported = FALSE; + TNC_MessageType msg_type; + TNC_UInt32 msg_flags; enumerator_t *enumerator; imv_t *imv; + msg_type = (msg_vid << 8) | msg_subtype; + enumerator = this->imvs->create_enumerator(this->imvs); while (enumerator->enumerate(enumerator, &imv)) { - if (imv->receive_message && imv->type_supported(imv, message_type)) + if (imv->type_supported(imv, msg_vid, msg_subtype) && + (!excl || (excl && imv->has_id(imv, dst_imv_id)) )) { - type_supported = TRUE; - imv->receive_message(imv->get_id(imv), connection_id, - message, message_len, message_type); + if (imv->receive_message_long && src_imc_id) + { + type_supported = TRUE; + msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0; + imv->receive_message_long(imv->get_id(imv), connection_id, + msg_flags, msg, msg_len, msg_vid, msg_subtype, + src_imc_id, dst_imv_id); + + } + else if (imv->receive_message && msg_vid <= TNC_VENDORID_ANY && + msg_subtype <= TNC_SUBTYPE_ANY) + { + type_supported = TRUE; + msg_type = (msg_vid << 8) | msg_subtype; + imv->receive_message(imv->get_id(imv), connection_id, + msg, msg_len, msg_type); + } } } enumerator->destroy(enumerator); if (!type_supported) { - DBG2(DBG_TNC, "message type 0x%08x not supported by any IMV", message_type); + DBG2(DBG_TNC, "message type 0x%06x/0x%08x not supported by any IMV", + msg_vid, msg_subtype); } } @@ -359,11 +434,13 @@ imv_manager_t* tnc_imv_manager_create(void) .remove = _remove_, /* avoid name conflict with stdio.h */ .load = _load, .is_registered = _is_registered, + .reserve_id = _reserve_id, .get_recommendation_policy = _get_recommendation_policy, .create_recommendations = _create_recommendations, .enforce_recommendation = _enforce_recommendation, .notify_connection_change = _notify_connection_change, .set_message_types = _set_message_types, + .set_message_types_long = _set_message_types_long, .solicit_recommendation = _solicit_recommendation, .receive_message = _receive_message, .batch_ending = _batch_ending, diff --git a/src/libcharon/plugins/tnc_pdp/Makefile.am b/src/libcharon/plugins/tnc_pdp/Makefile.am new file mode 100644 index 000000000..2d4c4d55a --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/Makefile.am @@ -0,0 +1,24 @@ + +INCLUDES = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon \ + -I$(top_srcdir)/src/libradius + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-tnc-pdp.la +else +plugin_LTLIBRARIES = libstrongswan-tnc-pdp.la +libstrongswan_tnc_pdp_la_LIBADD = \ + $(top_builddir)/src/libradius/libradius.la \ + $(top_builddir)/src/libtls/libtls.la \ + $(top_builddir)/src/libtnccs/libtnccs.la +endif + +libstrongswan_tnc_pdp_la_SOURCES = \ + tnc_pdp_plugin.h tnc_pdp_plugin.c \ + tnc_pdp.h tnc_pdp.c tnc_pdp_connections.h tnc_pdp_connections.c + +libstrongswan_tnc_pdp_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c new file mode 100644 index 000000000..0625baa90 --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2012 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 "tnc_pdp.h" +#include "tnc_pdp_connections.h" + +#include <errno.h> +#include <unistd.h> + +#include <radius_message.h> +#include <radius_mppe.h> + +#include <daemon.h> +#include <debug.h> +#include <pen/pen.h> +#include <threading/thread.h> +#include <processing/jobs/callback_job.h> +#include <sa/authenticators/eap/eap_method.h> + +typedef struct private_tnc_pdp_t private_tnc_pdp_t; + +/** + * Maximum size of a RADIUS IP packet + */ +#define MAX_PACKET 4096 + +/** + * private data of tnc_pdp_t + */ +struct private_tnc_pdp_t { + + /** + * implements tnc_pdp_t interface + */ + tnc_pdp_t public; + + /** + * ID of the server + */ + identification_t *server; + + /** + * EAP method type to be used + */ + eap_type_t type; + + /** + * IPv4 RADIUS socket + */ + int ipv4; + + /** + * IPv6 RADIUS socket + */ + int ipv6; + + /** + * Callback job dispatching commands + */ + callback_job_t *job; + + /** + * RADIUS shared secret + */ + chunk_t secret; + + /** + * MD5 hasher + */ + hasher_t *hasher; + + /** + * HMAC MD5 signer, with secret set + */ + signer_t *signer; + + /** + * Random number generator for MS-MPPE salt values + */ + rng_t *rng; + + /** + * List of registered TNC-PDP connections + */ + tnc_pdp_connections_t *connections; +}; + + +/** + * Open IPv4 or IPv6 UDP RADIUS socket + */ +static int open_socket(int family, u_int16_t port) +{ + int on = TRUE; + struct sockaddr_storage addr; + socklen_t addrlen; + int skt; + + memset(&addr, 0, sizeof(addr)); + addr.ss_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); + 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); + break; + } + default: + return 0; + } + + /* open the socket */ + skt = socket(family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 0) + { + DBG1(DBG_CFG, "opening RADIUS socket failed: %s", strerror(errno)); + return 0; + } + if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + { + DBG1(DBG_CFG, "unable to set SO_REUSEADDR on socket: %s", strerror(errno)); + close(skt); + return 0; + } + + /* bind the socket */ + if (bind(skt, (struct sockaddr *)&addr, addrlen) < 0) + { + DBG1(DBG_CFG, "unable to bind RADIUS socket: %s", strerror(errno)); + close(skt); + return 0; + } + + return skt; +} + +/** + * Send a RADIUS message to client + */ +static void send_message(private_tnc_pdp_t *this, radius_message_t *message, + host_t *client) +{ + int fd; + chunk_t data; + + fd = (client->get_family(client) == AF_INET) ? this->ipv4 : this->ipv6; + data = message->get_encoding(message); + + DBG2(DBG_CFG, "sending RADIUS packet to %#H", client); + DBG3(DBG_CFG, "%B", &data); + + if (sendto(fd, data.ptr, data.len, 0, client->get_sockaddr(client), + *client->get_sockaddr_len(client)) != data.len) + { + DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno)); + } +} + +/** + * Encrypt a MS-MPPE-Send/Recv-Key + */ +static chunk_t encrypt_mppe_key(private_tnc_pdp_t *this, u_int8_t type, + chunk_t key, u_int16_t *salt, + radius_message_t *request) +{ + chunk_t a, r, seed, data; + u_char b[HASH_SIZE_MD5], *c; + mppe_key_t *mppe_key; + + /** + * From RFC2548 (encryption): + * b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1) + * b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2) + * . . . + * b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i) + */ + + data = chunk_alloc(sizeof(mppe_key_t) + + HASH_SIZE_MD5 * (1 + key.len / HASH_SIZE_MD5)); + memset(data.ptr, 0x00, data.len); + + mppe_key = (mppe_key_t*)data.ptr; + mppe_key->id = htonl(PEN_MICROSOFT); + mppe_key->type = type; + mppe_key->length = data.len - sizeof(mppe_key->id); + mppe_key->key[0] = key.len; + + memcpy(&mppe_key->key[1], key.ptr, key.len); + + /** + * generate a 16 bit unique random salt value for the MPPE stream cipher + * the MSB of the salt MUST be set to 1 + */ + a = chunk_create((u_char*)&(mppe_key->salt), sizeof(mppe_key->salt)); + do + { + this->rng->get_bytes(this->rng, a.len, a.ptr); + *a.ptr |= 0x80; + } + while (mppe_key->salt == *salt); + + /* update the salt value */ + *salt = mppe_key->salt; + + r = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5); + seed = chunk_cata("cc", r, a); + + c = mppe_key->key; + while (c < data.ptr + data.len) + { + /* b(i) = MD5(S + c(i-1)) */ + this->hasher->get_hash(this->hasher, this->secret, NULL); + this->hasher->get_hash(this->hasher, seed, b); + + /* c(i) = b(i) xor p(1) */ + memxor(c, b, HASH_SIZE_MD5); + + /* prepare next round */ + seed = chunk_create(c, HASH_SIZE_MD5); + c += HASH_SIZE_MD5; + } + + return data; +} + +/** + * Send a RADIUS response for a request + */ +static void send_response(private_tnc_pdp_t *this, radius_message_t *request, + radius_message_code_t code, eap_payload_t *eap, + identification_t *group, chunk_t msk, host_t *client) +{ + radius_message_t *response; + chunk_t data, recv, send; + u_int32_t tunnel_type; + u_int16_t salt = 0; + + response = radius_message_create(code); + if (eap) + { + data = eap->get_data(eap); + DBG3(DBG_CFG, "%N payload %B", eap_type_names, this->type, &data); + + /* fragment data suitable for RADIUS */ + while (data.len > MAX_RADIUS_ATTRIBUTE_SIZE) + { + response->add(response, RAT_EAP_MESSAGE, + chunk_create(data.ptr, MAX_RADIUS_ATTRIBUTE_SIZE)); + data = chunk_skip(data, MAX_RADIUS_ATTRIBUTE_SIZE); + } + response->add(response, RAT_EAP_MESSAGE, data); + } + if (group) + { + tunnel_type = RADIUS_TUNNEL_TYPE_ESP; + htoun32(data.ptr, tunnel_type); + data.len = sizeof(tunnel_type); + response->add(response, RAT_TUNNEL_TYPE, data); + response->add(response, RAT_FILTER_ID, group->get_encoding(group)); + } + if (msk.len) + { + recv = chunk_create(msk.ptr, msk.len / 2); + data = encrypt_mppe_key(this, MS_MPPE_RECV_KEY, recv, &salt, request); + response->add(response, RAT_VENDOR_SPECIFIC, data); + chunk_free(&data); + + send = chunk_create(msk.ptr + recv.len, msk.len - recv.len); + data = encrypt_mppe_key(this, MS_MPPE_SEND_KEY, send, &salt, request); + response->add(response, RAT_VENDOR_SPECIFIC, data); + chunk_free(&data); + } + response->set_identifier(response, request->get_identifier(request)); + response->sign(response, request->get_authenticator(request), + this->secret, this->hasher, this->signer, NULL, TRUE); + + DBG1(DBG_CFG, "sending RADIUS %N to client '%H'", radius_message_code_names, + code, client); + send_message(this, response, client); + response->destroy(response); +} + +/** + * Process EAP message + */ +static void process_eap(private_tnc_pdp_t *this, radius_message_t *request, + host_t *source) +{ + enumerator_t *enumerator; + eap_payload_t *in, *out = NULL; + eap_method_t *method; + eap_type_t eap_type; + u_int32_t eap_vendor; + chunk_t data, message = chunk_empty, msk = chunk_empty; + chunk_t user_name = chunk_empty, nas_id = chunk_empty; + identification_t *group = NULL; + radius_message_code_t code = RMC_ACCESS_CHALLENGE; + int type; + + enumerator = request->create_enumerator(request); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case RAT_USER_NAME: + user_name = data; + break; + case RAT_NAS_IDENTIFIER: + nas_id = data; + break; + case RAT_EAP_MESSAGE: + if (data.len) + { + message = chunk_cat("mc", message, data); + } + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + if (message.len) + { + in = eap_payload_create_data(message); + + /* apply EAP method selected by RADIUS server */ + eap_type = in->get_type(in, &eap_vendor); + + DBG3(DBG_CFG, "%N payload %B", eap_type_names, eap_type, &message); + + if (eap_type == EAP_IDENTITY) + { + identification_t *peer; + chunk_t eap_identity; + + if (message.len < 5) + { + goto end; + } + eap_identity = chunk_create(message.ptr + 5, message.len - 5); + peer = identification_create_from_data(eap_identity); + method = charon->eap->create_instance(charon->eap, this->type, + 0, EAP_SERVER, this->server, peer); + if (!method) + { + peer->destroy(peer); + goto end; + } + this->connections->add(this->connections, nas_id, user_name, peer, + method); + method->initiate(method, &out); + } + else + { + ike_sa_t *ike_sa; + auth_cfg_t *auth; + auth_rule_t type; + identification_t *data; + enumerator_t *e; + + method = this->connections->get_state(this->connections, nas_id, + user_name, &ike_sa); + if (!method) + { + goto end; + } + charon->bus->set_sa(charon->bus, ike_sa); + + switch (method->process(method, in, &out)) + { + case NEED_MORE: + code = RMC_ACCESS_CHALLENGE; + break; + case SUCCESS: + code = RMC_ACCESS_ACCEPT; + method->get_msk(method, &msk); + auth = ike_sa->get_auth_cfg(ike_sa, FALSE); + e = auth->create_enumerator(auth); + while (e->enumerate(e, &type, &data)) + { + /* look for group memberships */ + if (type == AUTH_RULE_GROUP) + { + group = data; + } + } + e->destroy(e); + + DESTROY_IF(out); + out = eap_payload_create_code(EAP_SUCCESS, + in->get_identifier(in)); + break; + case FAILED: + default: + code = RMC_ACCESS_REJECT; + DESTROY_IF(out); + out = eap_payload_create_code(EAP_FAILURE, + in->get_identifier(in)); + } + charon->bus->set_sa(charon->bus, NULL); + } + + 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); + } + +end: + free(message.ptr); + in->destroy(in); + } +} + +/** + * Process packets received on the RADIUS socket + */ +static job_requeue_t receive(private_tnc_pdp_t *this) +{ + while (TRUE) + { + radius_message_t *request; + char buffer[MAX_PACKET]; + int max_fd = 0, selected = 0, bytes_read = 0; + fd_set rfds; + bool oldstate; + host_t *source; + struct msghdr msg; + struct iovec iov; + union { + struct sockaddr_in in4; + struct sockaddr_in6 in6; + } src; + + FD_ZERO(&rfds); + + if (this->ipv4) + { + FD_SET(this->ipv4, &rfds); + } + if (this->ipv6) + { + FD_SET(this->ipv6, &rfds); + } + max_fd = max(this->ipv4, this->ipv6); + + DBG2(DBG_CFG, "waiting for data on RADIUS sockets"); + oldstate = thread_cancelability(TRUE); + if (select(max_fd + 1, &rfds, NULL, NULL, NULL) <= 0) + { + thread_cancelability(oldstate); + continue; + } + thread_cancelability(oldstate); + + if (FD_ISSET(this->ipv4, &rfds)) + { + selected = this->ipv4; + } + else if (FD_ISSET(this->ipv6, &rfds)) + { + selected = this->ipv6; + } + else + { + /* oops, shouldn't happen */ + continue; + } + + /* read received packet */ + msg.msg_name = &src; + msg.msg_namelen = sizeof(src); + iov.iov_base = buffer; + iov.iov_len = MAX_PACKET; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + bytes_read = recvmsg(selected, &msg, 0); + if (bytes_read < 0) + { + DBG1(DBG_CFG, "error reading RADIUS socket: %s", strerror(errno)); + continue; + } + if (msg.msg_flags & MSG_TRUNC) + { + DBG1(DBG_CFG, "receive buffer too small, RADIUS packet discarded"); + continue; + } + source = host_create_from_sockaddr((sockaddr_t*)&src); + DBG2(DBG_CFG, "received RADIUS packet from %#H", source); + DBG3(DBG_CFG, "%b", buffer, bytes_read); + request = radius_message_parse(chunk_create(buffer, bytes_read)); + if (request) + { + DBG1(DBG_CFG, "received RADIUS %N from client '%H'", + radius_message_code_names, request->get_code(request), source); + + if (request->verify(request, NULL, this->secret, this->hasher, + this->signer)) + { + process_eap(this, request, source); + } + request->destroy(request); + + } + else + { + DBG1(DBG_CFG, "received invalid RADIUS message, ignored"); + } + source->destroy(source); + } + return JOB_REQUEUE_FAIR; +} + +METHOD(tnc_pdp_t, destroy, void, + private_tnc_pdp_t *this) +{ + if (this->job) + { + this->job->cancel(this->job); + } + if (this->ipv4) + { + close(this->ipv4); + } + if (this->ipv6) + { + close(this->ipv6); + } + DESTROY_IF(this->server); + DESTROY_IF(this->signer); + DESTROY_IF(this->hasher); + DESTROY_IF(this->rng); + DESTROY_IF(this->connections); + free(this); +} + +/* + * see header file + */ +tnc_pdp_t *tnc_pdp_create(u_int16_t port) +{ + private_tnc_pdp_t *this; + char *secret, *server, *eap_type_str; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .ipv4 = open_socket(AF_INET, port), + .ipv6 = open_socket(AF_INET6, port), + .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5), + .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128), + .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), + .connections = tnc_pdp_connections_create(), + ); + + if (!this->hasher || !this->signer || !this->rng) + { + DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required"); + destroy(this); + return NULL; + } + if (!this->ipv4 && !this->ipv6) + { + DBG1(DBG_NET, "could not create any RADIUS sockets"); + destroy(this); + return NULL; + } + if (!this->ipv4) + { + DBG1(DBG_NET, "could not open IPv4 RADIUS socket, IPv4 disabled"); + } + if (!this->ipv6) + { + DBG1(DBG_NET, "could not open IPv6 RADIUS socket, IPv6 disabled"); + } + + server = lib->settings->get_str(lib->settings, + "charon.plugins.tnc-pdp.server", NULL); + if (!server) + { + DBG1(DBG_CFG, "missing PDP server name, PDP disabled"); + destroy(this); + return NULL; + } + this->server = identification_create_from_string(server); + + secret = lib->settings->get_str(lib->settings, + "charon.plugins.tnc-pdp.secret", NULL); + if (!secret) + { + DBG1(DBG_CFG, "missing RADIUS secret, PDP disabled"); + destroy(this); + return NULL; + } + this->secret = chunk_create(secret, strlen(secret)); + this->signer->set_key(this->signer, this->secret); + + eap_type_str = lib->settings->get_str(lib->settings, + "charon.plugins.tnc-pdp.method", "ttls"); + this->type = eap_type_from_string(eap_type_str); + if (this->type == 0) + { + DBG1(DBG_CFG, "unrecognized eap method \"%s\"", eap_type_str); + destroy(this); + return NULL; + } + DBG1(DBG_IKE, "eap method %N selected", eap_type_names, this->type); + + this->job = callback_job_create_with_prio((callback_job_cb_t)receive, + this, NULL, NULL, JOB_PRIO_CRITICAL); + lib->processor->queue_job(lib->processor, (job_t*)this->job); + + return &this->public; +} + diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp.h b/src/libcharon/plugins/tnc_pdp/tnc_pdp.h new file mode 100644 index 000000000..e769353b7 --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 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 tnc_pdp tnc_pdp + * @{ @ingroup tnc_pdp + */ + +#ifndef TNC_PDP_H_ +#define TNC_PDP_H_ + +typedef struct tnc_pdp_t tnc_pdp_t; + +#include <library.h> + +/** + * Public interface of a TNC Policy Decision Point object + */ +struct tnc_pdp_t { + + /** + * implements plugin interface + */ + void (*destroy)(tnc_pdp_t *this); +}; + +/** + * Create a TNC PDP instance + * + * @param port RADIUS port of TNC PDP + */ +tnc_pdp_t* tnc_pdp_create(u_int16_t port); + +#endif /** TNC_PDP_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c new file mode 100644 index 000000000..175a57aba --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 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 "tnc_pdp_connections.h" + +#include <utils/linked_list.h> +#include <debug.h> + +typedef struct private_tnc_pdp_connections_t private_tnc_pdp_connections_t; +typedef struct entry_t entry_t; + +/** + * Private data of tnc_pdp_connections_t + */ +struct private_tnc_pdp_connections_t { + + /** + * Implements tnc_pdp_connections_t interface + */ + tnc_pdp_connections_t public; + + /** + * List of TNC PEP RADIUS Connections + */ + linked_list_t *list; +}; + +/** + * Data entry for a TNC PEP RADIUS connection + */ +struct entry_t { + + /** + * NAS identifier of PEP + */ + chunk_t nas_id; + + /** + * User name of TNC Client + */ + chunk_t user_name; + + /** + * EAP method state + */ + eap_method_t *method; + + /** + * IKE SA used for bus communication + */ + ike_sa_t *ike_sa; +}; + +/** + * Free the memory allocated to a data entry + */ +static void free_entry(entry_t *this) +{ + this->method->destroy(this->method); + this->ike_sa->destroy(this->ike_sa); + free(this->nas_id.ptr); + free(this->user_name.ptr); + free(this); +} + +/** + * Find a matching data entry + */ +static bool equals_entry( entry_t *this, chunk_t nas_id, chunk_t user_name) +{ + bool no_nas_id = !this->nas_id.ptr && !nas_id.ptr; + + return (chunk_equals(this->nas_id, nas_id) || no_nas_id) && + chunk_equals(this->user_name, user_name); +} + +/** + * Find a matching data entry + */ +static void dbg_nas_user(chunk_t nas_id, chunk_t user_name, bool not, char *op) +{ + if (nas_id.len) + { + DBG1(DBG_CFG, "%s RADIUS connection for user '%.*s' NAS '%.*s'", + not ? "could not find" : op, user_name.len, user_name.ptr, + nas_id.len, nas_id.ptr); + } + else + { + DBG1(DBG_CFG, "%s RADIUS connection for user '%.*s'", + not ? "could not find" : op, user_name.len, user_name.ptr); + } +} + +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) +{ + enumerator_t *enumerator; + entry_t *entry; + ike_sa_id_t *ike_sa_id; + ike_sa_t *ike_sa; + bool found = FALSE; + + ike_sa_id = ike_sa_id_create(0, 0, FALSE); + ike_sa = ike_sa_create(ike_sa_id); + ike_sa_id->destroy(ike_sa_id); + ike_sa->set_other_id(ike_sa, peer); + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (equals_entry(entry, nas_id, user_name)) + { + found = TRUE; + entry->method->destroy(entry->method); + entry->ike_sa->destroy(entry->ike_sa); + DBG1(DBG_CFG, "removed stale RADIUS connection"); + entry->method = method; + entry->ike_sa = ike_sa; + break; + } + } + enumerator->destroy(enumerator); + + 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; + this->list->insert_last(this->list, entry); + } + dbg_nas_user(nas_id, user_name, FALSE, "created"); +} + +METHOD(tnc_pdp_connections_t, remove_, void, + private_tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name) +{ + enumerator_t *enumerator; + entry_t *entry; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (equals_entry(entry, nas_id, user_name)) + { + free_entry(entry); + this->list->remove_at(this->list, enumerator); + dbg_nas_user(nas_id, user_name, FALSE, "removed"); + break; + } + } + enumerator->destroy(enumerator); +} + +METHOD(tnc_pdp_connections_t, get_state, eap_method_t*, + private_tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name, + ike_sa_t **ike_sa) +{ + enumerator_t *enumerator; + entry_t *entry; + eap_method_t *found = NULL; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (equals_entry(entry, nas_id, user_name)) + { + found = entry->method; + *ike_sa = entry->ike_sa; + break; + } + } + enumerator->destroy(enumerator); + + dbg_nas_user(nas_id, user_name, !found, "found"); + return found; +} + +METHOD(tnc_pdp_connections_t, destroy, void, + private_tnc_pdp_connections_t *this) +{ + this->list->destroy_function(this->list, (void*)free_entry); + free(this); +} + +/* + * see header file + */ +tnc_pdp_connections_t *tnc_pdp_connections_create(void) +{ + private_tnc_pdp_connections_t *this; + + INIT(this, + .public = { + .add = _add, + .remove = _remove_, + .get_state = _get_state, + .destroy = _destroy, + }, + .list = linked_list_create(), + ); + + 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 new file mode 100644 index 000000000..b9f5d097b --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp_connections.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 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 tnc_pdp_connections tnc_pdp_connections + * @{ @ingroup tnc_pdp + */ + +#ifndef TNC_PDP_CONNECTIONS_H_ +#define TNC_PDP_CONNECTIONS_H_ + +typedef struct tnc_pdp_connections_t tnc_pdp_connections_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/authenticators/eap/eap_method.h> + +/** + * Public interface of a tnc_pdp_connections object + */ +struct tnc_pdp_connections_t { + + /** + * Register a new TNC PEP RADIUS Connection + * + * @param nas_id NAS identifier of Policy Enforcement Point + * @param user_name User name of TNC Client + * @param peer Peer identity + * @param method EAP method state for this TNC PEP Connection + */ + void (*add)(tnc_pdp_connections_t *this, chunk_t nas_id, chunk_t user_name, + identification_t *peer, eap_method_t *method); + + /** + * Remove a TNC PEP RADIUS Connection + * + * @param nas_id NAS identifier of Policy Enforcement Point + * @param user_name User name of TNC Client + */ + void (*remove)(tnc_pdp_connections_t *this, chunk_t nas_id, + chunk_t user_name); + + /** + * Get the EAP method and IKE_SA of a registered TNC PEP RADIUS Connection + * + * @param nas_id NAS identifier of Policy Enforcement Point + * @param user_name User name of TNC Client + * @param ike_sa IKE_SA used for bus communication only + * @return EAP method for this connection or NULL if not found + */ + eap_method_t* (*get_state)(tnc_pdp_connections_t *this, chunk_t nas_id, + chunk_t user_name, ike_sa_t **ike_sa); + + /** + * Destroys a tnc_pdp_connections_t object. + */ + void (*destroy)(tnc_pdp_connections_t *this); +}; + +/** + * Create a tnc_pdp_connections_t instance + */ +tnc_pdp_connections_t* tnc_pdp_connections_create(void); + +#endif /** TNC_PDP_CONNECTIONS_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_plugin.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp_plugin.c new file mode 100644 index 000000000..9abe02aec --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp_plugin.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 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 "tnc_pdp_plugin.h" +#include "tnc_pdp.h" + +typedef struct private_tnc_pdp_plugin_t private_tnc_pdp_plugin_t; + +/** + * Default RADIUS port, when not configured + */ +#define RADIUS_PORT 1812 + +/** + * private data of tnc_pdp plugin + */ +struct private_tnc_pdp_plugin_t { + + /** + * implements plugin interface + */ + tnc_pdp_plugin_t public; + + /** + * Policy Decision Point object + */ + tnc_pdp_t *pdp; + +}; + +METHOD(plugin_t, get_name, char*, + private_tnc_pdp_plugin_t *this) +{ + return "tnc-pdp"; +} + +METHOD(plugin_t, get_features, int, + private_tnc_pdp_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_PROVIDE(CUSTOM, "tnc-pdp"), + PLUGIN_DEPENDS(CUSTOM, "imv-manager"), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_tnc_pdp_plugin_t *this) +{ + DESTROY_IF(this->pdp); + free(this); +} + +/* + * see header file + */ +plugin_t *tnc_pdp_plugin_create() +{ + private_tnc_pdp_plugin_t *this; + int port; + + port = lib->settings->get_int(lib->settings, + "charon.plugins.tnc_pdp.port", RADIUS_PORT); + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + .pdp = tnc_pdp_create(port), + ); + + return &this->public.plugin; +} + diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp_plugin.h b/src/libcharon/plugins/tnc_pdp/tnc_pdp_plugin.h new file mode 100644 index 000000000..9b8b9ff0e --- /dev/null +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 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 tnc_pdp tnc_pdp + * @ingroup cplugins + * + * @defgroup tnc_pdp_plugin tnc_pdp_plugin + * @{ @ingroup tnc_pdp + */ + +#ifndef TNC_PDP_PLUGIN_H_ +#define TNC_PDP_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct tnc_pdp_plugin_t tnc_pdp_plugin_t; + +/** + * TNC-PDP plugin + */ +struct tnc_pdp_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** TNC_PDP_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c b/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c index f007ce19f..64ed160d9 100644 --- a/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c +++ b/src/libcharon/plugins/tnc_tnccs/tnc_tnccs_manager.c @@ -55,6 +55,11 @@ struct tnccs_connection_entry_t { TNC_ConnectionID id; /** + * TNCCS protocol type + */ + tnccs_type_t type; + + /** * TNCCS instance */ tnccs_t *tnccs; @@ -174,13 +179,14 @@ METHOD(tnccs_manager_t, create_instance, tnccs_t*, } METHOD(tnccs_manager_t, create_connection, TNC_ConnectionID, - private_tnc_tnccs_manager_t *this, tnccs_t *tnccs, + private_tnc_tnccs_manager_t *this, tnccs_type_t type, tnccs_t *tnccs, tnccs_send_message_t send_message, bool* request_handshake_retry, recommendations_t **recs) { tnccs_connection_entry_t *entry; entry = malloc_thing(tnccs_connection_entry_t); + entry->type = type; entry->tnccs = tnccs; entry->send_message = send_message; entry->request_handshake_retry = request_handshake_retry; @@ -295,24 +301,22 @@ METHOD(tnccs_manager_t, request_handshake_retry, TNC_Result, METHOD(tnccs_manager_t, send_message, TNC_Result, private_tnc_tnccs_manager_t *this, TNC_IMCID imc_id, TNC_IMVID imv_id, TNC_ConnectionID id, + TNC_UInt32 msg_flags, TNC_BufferReference msg, TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype) { enumerator_t *enumerator; tnccs_connection_entry_t *entry; tnccs_send_message_t send_message = NULL; tnccs_t *tnccs = NULL; - TNC_VendorID msg_vid; - TNC_MessageSubtype msg_subtype; - - msg_vid = (msg_type >> 8) & TNC_VENDORID_ANY; - msg_subtype = msg_type & TNC_SUBTYPE_ANY; if (msg_vid == TNC_VENDORID_ANY || msg_subtype == TNC_SUBTYPE_ANY) { - DBG1(DBG_TNC, "not sending message of invalid type 0x%08x", msg_type); + DBG1(DBG_TNC, "not sending message of invalid type 0x%02x/0x%08x", + msg_vid, msg_subtype); return TNC_RESULT_INVALID_PARAMETER; } @@ -332,7 +336,8 @@ METHOD(tnccs_manager_t, send_message, TNC_Result, if (tnccs && send_message) { - return send_message(tnccs, imc_id, imv_id, msg, msg_len, msg_type); + return send_message(tnccs, imc_id, imv_id, msg_flags, msg, msg_len, + msg_vid, msg_subtype); } return TNC_RESULT_FATAL; } @@ -368,20 +373,150 @@ METHOD(tnccs_manager_t, provide_recommendation, TNC_Result, return TNC_RESULT_FATAL; } +/** + * Write the value of a boolean attribute into the buffer + */ +static TNC_Result bool_attribute(TNC_UInt32 buffer_len, + TNC_BufferReference buffer, + TNC_UInt32 *value_len, + bool value) +{ + *value_len = 1; + + if (buffer && buffer_len > 0) + { + *buffer = value ? 0x01 : 0x00; + return TNC_RESULT_SUCCESS; + } + else + { + return TNC_RESULT_INVALID_PARAMETER; + } +} + +/** + * Write the value of an u_int32_t attribute into the buffer + */ +static TNC_Result uint_attribute(TNC_UInt32 buffer_len, + TNC_BufferReference buffer, + TNC_UInt32 *value_len, + u_int32_t value) +{ + *value_len = sizeof(u_int32_t); + + if (buffer && buffer_len >= *value_len) + { + htoun32(buffer, value); + return TNC_RESULT_SUCCESS; + } + else + { + return TNC_RESULT_INVALID_PARAMETER; + } +} + +/** + * Write the value of string attribute into the buffer + */ +static TNC_Result str_attribute(TNC_UInt32 buffer_len, + TNC_BufferReference buffer, + TNC_UInt32 *value_len, + char *value) +{ + *value_len = 1 + strlen(value); + + if (buffer && buffer_len >= *value_len) + { + snprintf(buffer, buffer_len, "%s", value); + return TNC_RESULT_SUCCESS; + } + else + { + return TNC_RESULT_INVALID_PARAMETER; + } +} + METHOD(tnccs_manager_t, get_attribute, TNC_Result, - private_tnc_tnccs_manager_t *this, TNC_IMVID imv_id, + private_tnc_tnccs_manager_t *this, bool is_imc, + TNC_UInt32 imcv_id, TNC_ConnectionID id, TNC_AttributeID attribute_id, TNC_UInt32 buffer_len, TNC_BufferReference buffer, - TNC_UInt32 *out_value_len) + TNC_UInt32 *value_len) { enumerator_t *enumerator; tnccs_connection_entry_t *entry; - recommendations_t *recs = NULL; + bool attribute_match = FALSE, entry_found = FALSE; + + if (is_imc) + { + switch (attribute_id) + { + /* these attributes are unsupported */ + case TNC_ATTRIBUTEID_SOHR: + case TNC_ATTRIBUTEID_SSOHR: + return TNC_RESULT_INVALID_PARAMETER; + + /* these attributes are supported */ + case TNC_ATTRIBUTEID_PRIMARY_IMC_ID: + attribute_match = TRUE; + break; + + /* these attributes are yet to be matched */ + default: + break; + } + } + else + { + switch (attribute_id) + { + /* these attributes are unsupported or invalid */ + case TNC_ATTRIBUTEID_REASON_STRING: + case TNC_ATTRIBUTEID_REASON_LANGUAGE: + case TNC_ATTRIBUTEID_SOH: + case TNC_ATTRIBUTEID_SSOH: + return TNC_RESULT_INVALID_PARAMETER; + + /* these attributes are supported */ + case TNC_ATTRIBUTEID_PRIMARY_IMV_ID: + attribute_match = TRUE; + break; + + /* these attributes are yet to be matched */ + default: + break; + } + } + + if (!attribute_match) + { + switch (attribute_id) + { + /* these attributes are supported */ + case TNC_ATTRIBUTEID_PREFERRED_LANGUAGE: + case TNC_ATTRIBUTEID_MAX_ROUND_TRIPS: + case TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE: + case TNC_ATTRIBUTEID_HAS_LONG_TYPES: + case TNC_ATTRIBUTEID_HAS_EXCLUSIVE: + case TNC_ATTRIBUTEID_HAS_SOH: + case TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL: + case TNC_ATTRIBUTEID_IFTNCCS_VERSION: + case TNC_ATTRIBUTEID_IFT_PROTOCOL: + case TNC_ATTRIBUTEID_IFT_VERSION: + break; - if (id == TNC_CONNECTIONID_ANY || - attribute_id != TNC_ATTRIBUTEID_PREFERRED_LANGUAGE) + /* these attributes are unsupported or unknown */ + case TNC_ATTRIBUTEID_DHPN: + case TNC_ATTRIBUTEID_TLS_UNIQUE: + default: + return TNC_RESULT_INVALID_PARAMETER; + } + } + + /* attributes specific to the TNCC or TNCS are unsupported */ + if (id == TNC_CONNECTIONID_ANY) { return TNC_RESULT_INVALID_PARAMETER; } @@ -392,34 +527,104 @@ METHOD(tnccs_manager_t, get_attribute, TNC_Result, { if (id == entry->id) { - recs = entry->recs; + entry_found = TRUE; break; } } enumerator->destroy(enumerator); this->connection_lock->unlock(this->connection_lock); - if (recs) + if (!entry_found) { - chunk_t pref_lang; + return TNC_RESULT_INVALID_PARAMETER; + } - pref_lang = recs->get_preferred_language(recs); - if (pref_lang.len == 0) + switch (attribute_id) + { + case TNC_ATTRIBUTEID_PREFERRED_LANGUAGE: { - return TNC_RESULT_INVALID_PARAMETER; + recommendations_t *recs; + chunk_t pref_lang; + + recs = entry->recs; + if (!recs) + { + return TNC_RESULT_INVALID_PARAMETER; + } + pref_lang = recs->get_preferred_language(recs); + if (pref_lang.len == 0) + { + return TNC_RESULT_INVALID_PARAMETER; + } + *value_len = pref_lang.len; + if (buffer && buffer_len >= pref_lang.len) + { + memcpy(buffer, pref_lang.ptr, pref_lang.len); + } + return TNC_RESULT_SUCCESS; } - *out_value_len = pref_lang.len; - if (buffer && buffer_len >= pref_lang.len) + case TNC_ATTRIBUTEID_MAX_ROUND_TRIPS: + return uint_attribute(buffer_len, buffer, value_len, 0xffffffff); + case TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE: + return uint_attribute(buffer_len, buffer, value_len, 0x00000000); + case TNC_ATTRIBUTEID_HAS_LONG_TYPES: + case TNC_ATTRIBUTEID_HAS_EXCLUSIVE: + return bool_attribute(buffer_len, buffer, value_len, + entry->type == TNCCS_2_0); + case TNC_ATTRIBUTEID_HAS_SOH: + return bool_attribute(buffer_len, buffer, value_len, + entry->type == TNCCS_SOH); + case TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL: { - memcpy(buffer, pref_lang.ptr, pref_lang.len); + char *protocol; + + switch (entry->type) + { + case TNCCS_1_1: + case TNCCS_2_0: + protocol = "IF-TNCCS"; + break; + case TNCCS_SOH: + protocol = "IF-TNCCS-SOH"; + break; + default: + return TNC_RESULT_INVALID_PARAMETER; + } + return str_attribute(buffer_len, buffer, value_len, protocol); } - return TNC_RESULT_SUCCESS; + case TNC_ATTRIBUTEID_IFTNCCS_VERSION: + { + char *version; + + switch (entry->type) + { + case TNCCS_1_1: + version = "1.1"; + break; + case TNCCS_2_0: + version = "2.0"; + break; + case TNCCS_SOH: + version = "1.0"; + break; + default: + 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"); + case TNC_ATTRIBUTEID_IFT_VERSION: + return str_attribute(buffer_len, buffer, value_len, "1.1"); + default: + return TNC_RESULT_INVALID_PARAMETER; } - return TNC_RESULT_INVALID_PARAMETER; } METHOD(tnccs_manager_t, set_attribute, TNC_Result, - private_tnc_tnccs_manager_t *this, TNC_IMVID imv_id, + private_tnc_tnccs_manager_t *this, bool is_imc, + TNC_UInt32 imcv_id, TNC_ConnectionID id, TNC_AttributeID attribute_id, TNC_UInt32 buffer_len, @@ -429,7 +634,7 @@ METHOD(tnccs_manager_t, set_attribute, TNC_Result, tnccs_connection_entry_t *entry; recommendations_t *recs = NULL; - if (id == TNC_CONNECTIONID_ANY || + if (is_imc || id == TNC_CONNECTIONID_ANY || (attribute_id != TNC_ATTRIBUTEID_REASON_STRING && attribute_id != TNC_ATTRIBUTEID_REASON_LANGUAGE)) { @@ -455,11 +660,11 @@ METHOD(tnccs_manager_t, set_attribute, TNC_Result, if (attribute_id == TNC_ATTRIBUTEID_REASON_STRING) { - return recs->set_reason_string(recs, imv_id, attribute); + return recs->set_reason_string(recs, imcv_id, attribute); } else { - return recs->set_reason_language(recs, imv_id, attribute); + return recs->set_reason_language(recs, imcv_id, attribute); } } 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 88a2c8474..3673221e5 100644 --- a/src/libcharon/plugins/tnccs_11/tnccs_11.c +++ b/src/libcharon/plugins/tnccs_11/tnccs_11.c @@ -100,12 +100,14 @@ struct private_tnccs_11_t { METHOD(tnccs_t, send_msg, TNC_Result, private_tnccs_11_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id, + TNC_UInt32 msg_flags, TNC_BufferReference msg, TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype) { tnccs_msg_t *tnccs_msg; - u_int32_t vendor_id, subtype; + TNC_MessageType msg_type; enum_name_t *pa_subtype_names; if (!this->send_msg) @@ -115,18 +117,23 @@ METHOD(tnccs_t, send_msg, TNC_Result, this->is_server ? imv_id : imc_id); return TNC_RESULT_ILLEGAL_OPERATION; } - vendor_id = msg_type >> 8; - subtype = msg_type & 0xff; - pa_subtype_names = get_pa_subtype_names(vendor_id); + if (msg_vid > TNC_VENDORID_ANY || msg_subtype > TNC_SUBTYPE_ANY) + { + return TNC_RESULT_NO_LONG_MESSAGE_TYPES; + } + msg_type = (msg_vid << 8) | msg_subtype; + + pa_subtype_names = get_pa_subtype_names(msg_vid); if (pa_subtype_names) { DBG2(DBG_TNC, "creating IMC-IMV message type '%N/%N' 0x%06x/0x%02x", - pen_names, vendor_id, pa_subtype_names, subtype, vendor_id, subtype); + pen_names, msg_vid, pa_subtype_names, msg_subtype, + msg_vid, msg_subtype); } else { DBG2(DBG_TNC, "creating IMC-IMV message type '%N' 0x%06x/0x%02x", - pen_names, vendor_id, vendor_id, subtype); + pen_names, msg_vid, msg_vid, msg_subtype); } tnccs_msg = imc_imv_msg_create(msg_type, chunk_create(msg, msg_len)); @@ -153,38 +160,40 @@ static void handle_message(private_tnccs_11_t *this, tnccs_msg_t *msg) imc_imv_msg_t *imc_imv_msg; TNC_MessageType msg_type; chunk_t msg_body; - u_int32_t vendor_id, subtype; + u_int32_t msg_vid, msg_subtype; enum_name_t *pa_subtype_names; imc_imv_msg = (imc_imv_msg_t*)msg; msg_type = imc_imv_msg->get_msg_type(imc_imv_msg); msg_body = imc_imv_msg->get_msg_body(imc_imv_msg); - vendor_id = msg_type >> 8; - subtype = msg_type & 0xff; + msg_vid = (msg_type >> 8) & TNC_VENDORID_ANY; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; - pa_subtype_names = get_pa_subtype_names(vendor_id); + pa_subtype_names = get_pa_subtype_names(msg_vid); if (pa_subtype_names) { DBG2(DBG_TNC, "handling IMC-IMV message type '%N/%N' 0x%06x/0x%02x", - pen_names, vendor_id, pa_subtype_names, subtype, - vendor_id, subtype); + pen_names, msg_vid, pa_subtype_names, msg_subtype, + msg_vid, msg_subtype); } else { DBG2(DBG_TNC, "handling IMC-IMV message type '%N' 0x%06x/0x%02x", - pen_names, vendor_id, vendor_id, subtype); + pen_names, msg_vid, msg_vid, msg_subtype); } this->send_msg = TRUE; if (this->is_server) { - tnc->imvs->receive_message(tnc->imvs, - this->connection_id, msg_body.ptr, msg_body.len, msg_type); + tnc->imvs->receive_message(tnc->imvs, this->connection_id, + FALSE, msg_body.ptr, msg_body.len, + msg_vid, msg_subtype, 0, TNC_IMVID_ANY); } else { - tnc->imcs->receive_message(tnc->imcs, - this->connection_id, msg_body.ptr, msg_body.len,msg_type); + tnc->imcs->receive_message(tnc->imcs, this->connection_id, + FALSE, msg_body.ptr, msg_body.len, + msg_vid, msg_subtype, 0, TNC_IMCID_ANY); } this->send_msg = FALSE; break; @@ -280,7 +289,7 @@ METHOD(tls_t, process, status_t, if (this->is_server && !this->connection_id) { this->connection_id = tnc->tnccs->create_connection(tnc->tnccs, - (tnccs_t*)this, _send_msg, + TNCCS_1_1, (tnccs_t*)this, _send_msg, &this->request_handshake_retry, &this->recs); if (!this->connection_id) { @@ -406,7 +415,7 @@ METHOD(tls_t, build, status_t, char *pref_lang; this->connection_id = tnc->tnccs->create_connection(tnc->tnccs, - (tnccs_t*)this, _send_msg, + TNCCS_1_1, (tnccs_t*)this, _send_msg, &this->request_handshake_retry, NULL); if (!this->connection_id) { 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 b9bbf6bd1..1c4913e5e 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c +++ b/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.c @@ -211,12 +211,6 @@ METHOD(pb_pa_msg_t, get_exclusive_flag, bool, return this->excl; } -METHOD(pb_pa_msg_t, set_exclusive_flag, void, - private_pb_pa_msg_t *this, bool excl) -{ - this->excl = excl; -} - /** * See header */ @@ -237,7 +231,6 @@ pb_tnc_msg_t *pb_pa_msg_create_from_data(chunk_t data) .get_validator_id = _get_validator_id, .get_body = _get_body, .get_exclusive_flag = _get_exclusive_flag, - .set_exclusive_flag = _set_exclusive_flag, }, .type = PB_MSG_PA, .encoding = chunk_clone(data), @@ -251,7 +244,7 @@ pb_tnc_msg_t *pb_pa_msg_create_from_data(chunk_t data) */ pb_tnc_msg_t *pb_pa_msg_create(u_int32_t vendor_id, u_int32_t subtype, u_int16_t collector_id, u_int16_t validator_id, - chunk_t msg_body) + bool excl, chunk_t msg_body) { private_pb_pa_msg_t *this; @@ -269,13 +262,13 @@ pb_tnc_msg_t *pb_pa_msg_create(u_int32_t vendor_id, u_int32_t subtype, .get_validator_id = _get_validator_id, .get_body = _get_body, .get_exclusive_flag = _get_exclusive_flag, - .set_exclusive_flag = _set_exclusive_flag, }, .type = PB_MSG_PA, .vendor_id = vendor_id, .subtype = subtype, .collector_id = collector_id, .validator_id = validator_id, + .excl = excl, .msg_body = chunk_clone(msg_body), ); diff --git a/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.h b/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.h index eb087e9e7..d9db9a1ce 100644 --- a/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.h +++ b/src/libcharon/plugins/tnccs_20/messages/pb_pa_msg.h @@ -71,12 +71,6 @@ struct pb_pa_msg_t { */ bool (*get_exclusive_flag)(pb_pa_msg_t *this); - /** - * Set the exclusive flag - * - * @param excl vexclusive flag - */ - void (*set_exclusive_flag)(pb_pa_msg_t *this, bool excl); }; /** @@ -86,11 +80,12 @@ struct pb_pa_msg_t { * @param subtype PA Subtype * @param collector_id Posture Collector ID * @param validator_id Posture Validator ID + * @param excl Exclusive Flag * @param msg_body PA Message Body */ pb_tnc_msg_t *pb_pa_msg_create(u_int32_t vendor_id, u_int32_t subtype, u_int16_t collector_id, u_int16_t validator_id, - chunk_t msg_body); + bool excl, chunk_t msg_body); /** * Create an unprocessed PB-PA 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 d37510880..606fc529b 100644 --- a/src/libcharon/plugins/tnccs_20/tnccs_20.c +++ b/src/libcharon/plugins/tnccs_20/tnccs_20.c @@ -99,15 +99,16 @@ struct private_tnccs_20_t { METHOD(tnccs_t, send_msg, TNC_Result, private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id, + TNC_UInt32 msg_flags, TNC_BufferReference msg, TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype) { - TNC_MessageSubtype msg_sub_type; - TNC_VendorID msg_vendor_id; pb_tnc_msg_t *pb_tnc_msg; pb_tnc_batch_type_t batch_type; enum_name_t *pa_subtype_names; + bool excl; if (!this->send_msg) { @@ -116,24 +117,22 @@ METHOD(tnccs_t, send_msg, TNC_Result, this->is_server ? imv_id : imc_id); return TNC_RESULT_ILLEGAL_OPERATION; } + excl = (msg_flags & TNC_MESSAGE_FLAGS_EXCLUSIVE) != 0; - msg_sub_type = msg_type & TNC_SUBTYPE_ANY; - msg_vendor_id = (msg_type >> 8) & TNC_VENDORID_ANY; + pb_tnc_msg = pb_pa_msg_create(msg_vid, msg_subtype, imc_id, imv_id, + excl, chunk_create(msg, msg_len)); - pb_tnc_msg = pb_pa_msg_create(msg_vendor_id, msg_sub_type, imc_id, imv_id, - chunk_create(msg, msg_len)); - - pa_subtype_names = get_pa_subtype_names(msg_vendor_id); + pa_subtype_names = get_pa_subtype_names(msg_vid); if (pa_subtype_names) { - DBG2(DBG_TNC, "creating PB-PA message type '%N/%N' 0x%06x/0x%02x", - pen_names, msg_vendor_id, pa_subtype_names, msg_sub_type, - msg_vendor_id, msg_sub_type); + DBG2(DBG_TNC, "creating PB-PA message type '%N/%N' 0x%06x/0x%08x", + pen_names, msg_vid, pa_subtype_names, msg_subtype, + msg_vid, msg_subtype); } else { - DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%02x", - pen_names, msg_vendor_id, msg_vendor_id, msg_sub_type); + DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%08x", + pen_names, msg_vid, msg_vid, msg_subtype); } /* adding PA message to SDATA or CDATA batch only */ @@ -168,39 +167,44 @@ static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg) case PB_MSG_PA: { pb_pa_msg_t *pa_msg; - TNC_MessageType msg_type; - u_int32_t vendor_id, subtype; + u_int32_t msg_vid, msg_subtype; + u_int16_t imc_id, imv_id; chunk_t msg_body; + bool excl; enum_name_t *pa_subtype_names; pa_msg = (pb_pa_msg_t*)msg; - vendor_id = pa_msg->get_vendor_id(pa_msg, &subtype); - msg_type = (vendor_id << 8) | (subtype & 0xff); + msg_vid = pa_msg->get_vendor_id(pa_msg, &msg_subtype); msg_body = pa_msg->get_body(pa_msg); + imc_id = pa_msg->get_collector_id(pa_msg); + imv_id = pa_msg->get_validator_id(pa_msg); + excl = pa_msg->get_exclusive_flag(pa_msg); - pa_subtype_names = get_pa_subtype_names(vendor_id); + pa_subtype_names = get_pa_subtype_names(msg_vid); if (pa_subtype_names) { - DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%02x", - pen_names, vendor_id, pa_subtype_names, subtype, - vendor_id, subtype); + DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x", + pen_names, msg_vid, pa_subtype_names, msg_subtype, + msg_vid, msg_subtype); } else { - DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%02x", - pen_names, vendor_id, vendor_id, subtype); + DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x", + pen_names, msg_vid, msg_vid, msg_subtype); } this->send_msg = TRUE; if (this->is_server) { - tnc->imvs->receive_message(tnc->imvs, - this->connection_id, msg_body.ptr, msg_body.len, msg_type); + tnc->imvs->receive_message(tnc->imvs, this->connection_id, + excl, msg_body.ptr, msg_body.len, + msg_vid, msg_subtype, imc_id, imv_id); } else { - tnc->imcs->receive_message(tnc->imcs, - this->connection_id, msg_body.ptr, msg_body.len,msg_type); + tnc->imcs->receive_message(tnc->imcs, this->connection_id, + excl, msg_body.ptr, msg_body.len, + msg_vid, msg_subtype, imv_id, imc_id); } this->send_msg = FALSE; break; @@ -371,7 +375,7 @@ METHOD(tls_t, process, status_t, if (this->is_server && !this->connection_id) { this->connection_id = tnc->tnccs->create_connection(tnc->tnccs, - (tnccs_t*)this, _send_msg, + TNCCS_2_0, (tnccs_t*)this, _send_msg, &this->request_handshake_retry, &this->recs); if (!this->connection_id) { @@ -552,7 +556,7 @@ METHOD(tls_t, build, status_t, char *pref_lang; this->connection_id = tnc->tnccs->create_connection(tnc->tnccs, - (tnccs_t*)this, _send_msg, + TNCCS_2_0, (tnccs_t*)this, _send_msg, &this->request_handshake_retry, NULL); if (!this->connection_id) { diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index bcc98709c..5d0a5aea8 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2012 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -33,6 +33,7 @@ #include <processing/jobs/send_dpd_job.h> #include <processing/jobs/send_keepalive_job.h> #include <processing/jobs/rekey_ike_sa_job.h> +#include <sa/ikev2/tasks/ike_auth_lifetime.h> #ifdef ME #include <sa/ikev2/tasks/ike_me.h> @@ -195,9 +196,9 @@ struct private_ike_sa_t { linked_list_t *attributes; /** - * list of peers additional addresses, transmitted via MOBIKE + * list of peer's addresses, additional ones transmitted via MOBIKE */ - linked_list_t *additional_addresses; + linked_list_t *peer_addresses; /** * previously value of received DESTINATION_IP hash @@ -668,7 +669,6 @@ METHOD(ike_sa_t, set_state, void, lib->scheduler->schedule_job(lib->scheduler, job, t); DBG1(DBG_IKE, "maximum IKE_SA lifetime %ds", t); } - trigger_dpd = this->peer_cfg->get_dpd(this->peer_cfg); } break; @@ -770,28 +770,28 @@ METHOD(ike_sa_t, get_virtual_ip, host_t*, } } -METHOD(ike_sa_t, add_additional_address, void, +METHOD(ike_sa_t, add_peer_address, void, private_ike_sa_t *this, host_t *host) { - this->additional_addresses->insert_last(this->additional_addresses, host); + this->peer_addresses->insert_last(this->peer_addresses, host); } -METHOD(ike_sa_t, create_additional_address_enumerator, enumerator_t*, +METHOD(ike_sa_t, create_peer_address_enumerator, enumerator_t*, private_ike_sa_t *this) { - return this->additional_addresses->create_enumerator( - this->additional_addresses); + return this->peer_addresses->create_enumerator(this->peer_addresses); } -METHOD(ike_sa_t, remove_additional_addresses, void, +METHOD(ike_sa_t, clear_peer_addresses, void, private_ike_sa_t *this) { - enumerator_t *enumerator = create_additional_address_enumerator(this); + enumerator_t *enumerator = create_peer_address_enumerator(this); host_t *host; + while (enumerator->enumerate(enumerator, (void**)&host)) { - this->additional_addresses->remove_at(this->additional_addresses, - enumerator); + this->peer_addresses->remove_at(this->peer_addresses, + enumerator); host->destroy(host); } enumerator->destroy(enumerator); @@ -1386,17 +1386,25 @@ METHOD(ike_sa_t, reauth, status_t, #endif /* ME */ ) { - time_t now = time_monotonic(NULL); + time_t del, now; - DBG1(DBG_IKE, "IKE_SA will timeout in %V", - &now, &this->stats[STAT_DELETE]); + del = this->stats[STAT_DELETE]; + now = time_monotonic(NULL); + DBG1(DBG_IKE, "IKE_SA %s[%d] will timeout in %V", + get_name(this), this->unique_id, &now, &del); return FAILED; } else { - DBG1(DBG_IKE, "reauthenticating actively"); + DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d] actively", + get_name(this), this->unique_id); } } + else + { + DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d]", + get_name(this), this->unique_id); + } this->task_manager->queue_ike_reauth(this->task_manager); return this->task_manager->initiate(this->task_manager); } @@ -1558,6 +1566,7 @@ METHOD(ike_sa_t, retransmit, status_t, DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)", this->keyingtry + 1, tries); reset(this); + resolve_hosts(this); this->task_manager->queue_ike(this->task_manager); return this->task_manager->initiate(this->task_manager); } @@ -1579,35 +1588,67 @@ METHOD(ike_sa_t, retransmit, status_t, return SUCCESS; } -METHOD(ike_sa_t, set_auth_lifetime, void, +METHOD(ike_sa_t, set_auth_lifetime, status_t, private_ike_sa_t *this, u_int32_t lifetime) { - u_int32_t reduction = this->peer_cfg->get_over_time(this->peer_cfg); - u_int32_t reauth_time = time_monotonic(NULL) + lifetime - reduction; + u_int32_t diff, hard, soft, now; + ike_auth_lifetime_t *task; + bool send_update; + + diff = this->peer_cfg->get_over_time(this->peer_cfg); + now = time_monotonic(NULL); + hard = now + lifetime; + soft = hard - diff; - if (lifetime < reduction) + /* check if we have to send an AUTH_LIFETIME to enforce the new lifetime. + * We send the notify in IKE_AUTH if not yet ESTABLISHED. */ + send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 && + !has_condition(this, COND_ORIGINAL_INITIATOR) && + (this->other_virtual_ip != NULL || + has_condition(this, COND_EAP_AUTHENTICATED)); + + if (lifetime < diff) { - DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, starting reauthentication", - lifetime); - lib->processor->queue_job(lib->processor, + this->stats[STAT_REAUTH] = now; + + if (!send_update) + { + DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, " + "starting reauthentication", lifetime); + lib->processor->queue_job(lib->processor, (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE)); + } } else if (this->stats[STAT_REAUTH] == 0 || - this->stats[STAT_REAUTH] > reauth_time) + this->stats[STAT_REAUTH] > soft) { - this->stats[STAT_REAUTH] = reauth_time; - DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication" - " in %ds", lifetime, lifetime - reduction); - lib->scheduler->schedule_job(lib->scheduler, + this->stats[STAT_REAUTH] = soft; + if (!send_update) + { + DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling " + "reauthentication in %ds", lifetime, lifetime - diff); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE), - lifetime - reduction); + lifetime - diff); + } } else { DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, " "reauthentication already scheduled in %ds", lifetime, this->stats[STAT_REAUTH] - time_monotonic(NULL)); + send_update = FALSE; } + /* give at least some seconds to reauthenticate */ + this->stats[STAT_DELETE] = max(hard, now + 10); + + if (send_update) + { + task = ike_auth_lifetime_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, &task->task); + return this->task_manager->initiate(this->task_manager); + } + return SUCCESS; } /** @@ -1639,25 +1680,20 @@ static bool is_any_path_valid(private_ike_sa_t *this) bool valid = FALSE; enumerator_t *enumerator; host_t *src, *addr; + DBG1(DBG_IKE, "old path is not available anymore, try to find another"); - src = hydra->kernel_interface->get_source_addr(hydra->kernel_interface, - this->other_host, NULL); - if (!src) + enumerator = this->peer_addresses->create_enumerator(this->peer_addresses); + while (enumerator->enumerate(enumerator, &addr)) { - enumerator = this->additional_addresses->create_enumerator( - this->additional_addresses); - while (enumerator->enumerate(enumerator, &addr)) + DBG1(DBG_IKE, "looking for a route to %H ...", addr); + src = hydra->kernel_interface->get_source_addr( + hydra->kernel_interface, addr, NULL); + if (src) { - DBG1(DBG_IKE, "looking for a route to %H ...", addr); - src = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, addr, NULL); - if (src) - { - break; - } + break; } - enumerator->destroy(enumerator); } + enumerator->destroy(enumerator); if (src) { valid = TRUE; @@ -1900,8 +1936,8 @@ METHOD(ike_sa_t, destroy, void, } this->other_virtual_ip->destroy(this->other_virtual_ip); } - this->additional_addresses->destroy_offset(this->additional_addresses, - offsetof(host_t, destroy)); + this->peer_addresses->destroy_offset(this->peer_addresses, + offsetof(host_t, destroy)); #ifdef ME if (this->is_mediation_server) { @@ -1990,9 +2026,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .has_condition = _has_condition, .set_pending_updates = _set_pending_updates, .get_pending_updates = _get_pending_updates, - .create_additional_address_enumerator = _create_additional_address_enumerator, - .add_additional_address = _add_additional_address, - .remove_additional_addresses = _remove_additional_addresses, + .create_peer_address_enumerator = _create_peer_address_enumerator, + .add_peer_address = _add_peer_address, + .clear_peer_addresses = _clear_peer_addresses, .has_mapping_changed = _has_mapping_changed, .retransmit = _retransmit, .delete = _delete_, @@ -2051,7 +2087,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .my_auths = linked_list_create(), .other_auths = linked_list_create(), .unique_id = ++unique_id, - .additional_addresses = linked_list_create(), + .peer_addresses = linked_list_create(), .attributes = linked_list_create(), .keepalive_interval = lib->settings->get_time(lib->settings, "charon.keep_alive", KEEPALIVE_INTERVAL), diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 3fc0a7be2..0644bab78 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2012 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -508,19 +508,19 @@ struct ike_sa_t { * * @param host host to add to list */ - void (*add_additional_address)(ike_sa_t *this, host_t *host); + void (*add_peer_address)(ike_sa_t *this, host_t *host); /** - * Create an enumerator over all additional addresses of the peer. + * Create an enumerator over all known addresses of the peer. * * @return enumerator over addresses */ - enumerator_t* (*create_additional_address_enumerator)(ike_sa_t *this); + enumerator_t* (*create_peer_address_enumerator)(ike_sa_t *this); /** - * Remove all additional addresses of the peer. + * Remove all known addresses of the peer. */ - void (*remove_additional_addresses)(ike_sa_t *this); + void (*clear_peer_addresses)(ike_sa_t *this); /** * Check if mappings have changed on a NAT for our source address. @@ -905,11 +905,15 @@ struct ike_sa_t { status_t (*reestablish) (ike_sa_t *this); /** - * Set the lifetime limit received from a AUTH_LIFETIME notify. + * Set the lifetime limit received/to send in a AUTH_LIFETIME notify. + * + * If the IKE_SA is already ESTABLISHED, an INFORMATIONAL is sent with + * an AUTH_LIFETIME notify. The call never fails on unestablished SAs. * * @param lifetime lifetime in seconds + * @return DESTROY_ME to destroy the IKE_SA */ - void (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); + status_t (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); /** * Set the virtual IP to use for this IKE_SA and its children. diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index d12f5c977..ba7fdd2da 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -162,15 +162,15 @@ struct private_task_manager_t { */ static void flush(private_task_manager_t *this) { - this->queued_tasks->destroy_offset(this->queued_tasks, - offsetof(task_t, destroy)); - this->queued_tasks = linked_list_create(); this->passive_tasks->destroy_offset(this->passive_tasks, offsetof(task_t, destroy)); this->passive_tasks = linked_list_create(); this->active_tasks->destroy_offset(this->active_tasks, offsetof(task_t, destroy)); this->active_tasks = linked_list_create(); + this->queued_tasks->destroy_offset(this->queued_tasks, + offsetof(task_t, destroy)); + this->queued_tasks = linked_list_create(); } /** @@ -369,6 +369,11 @@ METHOD(task_manager_t, initiate, status_t, exchange = INFORMATIONAL; break; } + if (activate_task(this, TASK_IKE_AUTH_LIFETIME)) + { + exchange = INFORMATIONAL; + break; + } #ifdef ME if (activate_task(this, TASK_IKE_ME)) { @@ -640,11 +645,9 @@ static status_t build_response(private_task_manager_t *this, message_t *request) enumerator); } break; + case DESTROY_ME: case FAILED: default: - charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); - /* FALL */ - case DESTROY_ME: /* destroy IKE_SA, but SEND response first */ delete = TRUE; break; @@ -679,6 +682,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) this->responding.packet->clone(this->responding.packet)); if (delete) { + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } return SUCCESS; diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c index 0de2efd38..7583710bf 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c +++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c @@ -51,7 +51,7 @@ struct private_ike_cert_pre_t { bool do_http_lookup; /** - * wheter this is the final authentication round + * whether this is the final authentication round */ bool final; }; diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index c533506bb..377714023 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2010-2012 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -134,13 +135,17 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) { if (first) { /* an ADDITIONAL_*_ADDRESS means replace, so flush once */ - this->ike_sa->remove_additional_addresses(this->ike_sa); + this->ike_sa->clear_peer_addresses(this->ike_sa); first = FALSE; + /* add the peer's current address to the list */ + host = this->ike_sa->get_other_host(this->ike_sa); + this->ike_sa->add_peer_address(this->ike_sa, + host->clone(host)); } data = notify->get_notification_data(notify); host = host_create_from_chunk(family, data, 0); DBG2(DBG_IKE, "got additional MOBIKE peer address: %H", host); - this->ike_sa->add_additional_address(this->ike_sa, host); + this->ike_sa->add_peer_address(this->ike_sa, host); this->addresses_updated = TRUE; break; } @@ -151,7 +156,10 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) } case NO_ADDITIONAL_ADDRESSES: { - this->ike_sa->remove_additional_addresses(this->ike_sa); + this->ike_sa->clear_peer_addresses(this->ike_sa); + /* add the peer's current address to the list */ + host = this->ike_sa->get_other_host(this->ike_sa); + this->ike_sa->add_peer_address(this->ike_sa, host->clone(host)); this->addresses_updated = TRUE; break; } @@ -291,18 +299,7 @@ METHOD(ike_mobike_t, transmit, void, other_old = this->ike_sa->get_other_host(this->ike_sa); ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); - me = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, other_old, NULL); - if (me) - { - apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg)); - DBG1(DBG_IKE, "checking original path %#H - %#H", me, other_old); - copy = packet->clone(packet); - copy->set_source(copy, me); - charon->sender->send(charon->sender, copy); - } - - enumerator = this->ike_sa->create_additional_address_enumerator(this->ike_sa); + enumerator = this->ike_sa->create_peer_address_enumerator(this->ike_sa); while (enumerator->enumerate(enumerator, (void**)&other)) { me = hydra->kernel_interface->get_source_addr( diff --git a/src/libcharon/sa/keymat.c b/src/libcharon/sa/keymat.c index d04d966ad..7ef0b9f5d 100644 --- a/src/libcharon/sa/keymat.c +++ b/src/libcharon/sa/keymat.c @@ -79,7 +79,9 @@ int keymat_get_keylen_integ(integrity_algorithm_t alg) { keylen_entry_t map[] = { {AUTH_HMAC_MD5_96, 128}, + {AUTH_HMAC_MD5_128, 128}, {AUTH_HMAC_SHA1_96, 160}, + {AUTH_HMAC_SHA1_160, 160}, {AUTH_HMAC_SHA2_256_96, 256}, {AUTH_HMAC_SHA2_256_128, 256}, {AUTH_HMAC_SHA2_384_192, 384}, diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 3f434dae1..9a6d4ebcf 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -74,8 +75,10 @@ typedef struct { peer_cfg_t *peer_cfg; /** ref to instanciated CHILD_SA */ child_sa_t *child_sa; + /** TRUE if an acquire is pending */ + bool pending; /** pending IKE_SA connecting upon acquire */ - ike_sa_t *pending; + ike_sa_t *ike_sa; } entry_t; /** @@ -170,10 +173,10 @@ METHOD(trap_manager_t, install, u_int32_t, } reqid = child_sa->get_reqid(child_sa); - entry = malloc_thing(entry_t); - entry->child_sa = child_sa; - entry->peer_cfg = peer->get_ref(peer); - entry->pending = NULL; + INIT(entry, + .child_sa = child_sa, + .peer_cfg = peer->get_ref(peer), + ); this->lock->write_lock(this->lock); this->traps->insert_last(this->traps, entry); @@ -263,38 +266,49 @@ METHOD(trap_manager_t, acquire, void, if (!found) { DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid); + this->lock->unlock(this->lock); + return; } - else if (found->pending) + if (!cas_bool(&found->pending, FALSE, TRUE)) { DBG1(DBG_CFG, "ignoring acquire, connection attempt pending"); + this->lock->unlock(this->lock); + return; } - else + peer = found->peer_cfg->get_ref(found->peer_cfg); + child = found->child_sa->get_config(found->child_sa); + child = child->get_ref(child); + reqid = found->child_sa->get_reqid(found->child_sa); + /* don't hold the lock while checking out the IKE_SA */ + this->lock->unlock(this->lock); + + ike_sa = charon->ike_sa_manager->checkout_by_config( + charon->ike_sa_manager, peer); + if (ike_sa) { - child = found->child_sa->get_config(found->child_sa); - peer = found->peer_cfg; - ike_sa = charon->ike_sa_manager->checkout_by_config( - charon->ike_sa_manager, peer); - if (ike_sa) + if (ike_sa->get_peer_cfg(ike_sa) == NULL) { - if (ike_sa->get_peer_cfg(ike_sa) == NULL) - { - ike_sa->set_peer_cfg(ike_sa, peer); - } - child->get_ref(child); - reqid = found->child_sa->get_reqid(found->child_sa); - if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) + ike_sa->set_peer_cfg(ike_sa, peer); + } + if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) + { + /* make sure the entry is still there */ + this->lock->read_lock(this->lock); + if (this->traps->find_first(this->traps, NULL, + (void**)&found) == SUCCESS) { - found->pending = ike_sa; - charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + found->ike_sa = ike_sa; } - else - { - charon->ike_sa_manager->checkin_and_destroy( + this->lock->unlock(this->lock); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin_and_destroy( charon->ike_sa_manager, ike_sa); - } } } - this->lock->unlock(this->lock); + peer->destroy(peer); } /** @@ -310,7 +324,7 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (entry->pending != ike_sa) + if (entry->ike_sa != ike_sa) { continue; } @@ -319,7 +333,8 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, { continue; } - entry->pending = NULL; + entry->ike_sa = NULL; + entry->pending = FALSE; } enumerator->destroy(enumerator); this->lock->unlock(this->lock); @@ -356,15 +371,21 @@ METHOD(listener_t, child_state_change, bool, METHOD(trap_manager_t, flush, void, private_trap_manager_t *this) { - this->traps->invoke_function(this->traps, (void*)destroy_entry); + linked_list_t *traps; + /* since destroying the CHILD_SA results in events which require a read + * lock we cannot destroy the list while holding the write lock */ + this->lock->write_lock(this->lock); + traps = this->traps; + this->traps = linked_list_create(); + this->lock->unlock(this->lock); + traps->destroy_function(traps, (void*)destroy_entry); } METHOD(trap_manager_t, destroy, void, private_trap_manager_t *this) { charon->bus->remove_listener(charon->bus, &this->listener.listener); - this->traps->invoke_function(this->traps, (void*)destroy_entry); - this->traps->destroy(this->traps); + this->traps->destroy_function(this->traps, (void*)destroy_entry); this->lock->destroy(this->lock); free(this); } diff --git a/src/libfast/dispatcher.c b/src/libfast/dispatcher.c index 8cfad0fd3..e5fca7074 100644 --- a/src/libfast/dispatcher.c +++ b/src/libfast/dispatcher.c @@ -183,8 +183,8 @@ static session_entry_t *session_entry_create(private_dispatcher_t *this, INIT(entry, .cond = condvar_create(CONDVAR_TYPE_DEFAULT), .session = load_session(this), - .used = time_monotonic(NULL), .host = strdup(host), + .used = time_monotonic(NULL), ); return entry; } diff --git a/src/libfast/smtp.h b/src/libfast/smtp.h index 910f18127..9589ea2a6 100644 --- a/src/libfast/smtp.h +++ b/src/libfast/smtp.h @@ -34,7 +34,7 @@ struct smtp_t { * Send an e-mail message. * * @param from sender address - * @param to receipient address + * @param to recipient address * @param subject mail subject * @param fmt mail body format string * @param ... arguments for body format string diff --git a/src/libfreeswan/Android.mk b/src/libfreeswan/Android.mk index e9805f0a3..a834d4846 100644 --- a/src/libfreeswan/Android.mk +++ b/src/libfreeswan/Android.mk @@ -26,6 +26,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := libfreeswan +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libfreeswan/Makefile.am b/src/libfreeswan/Makefile.am index bc12fe386..b38343d34 100644 --- a/src/libfreeswan/Makefile.am +++ b/src/libfreeswan/Makefile.am @@ -19,3 +19,4 @@ dist_man3_MANS = anyaddr.3 atoaddr.3 atoasr.3 atoul.3 goodmask.3 initaddr.3 init portof.3 rangetosubnet.3 sameaddr.3 subnetof.3 \ ttoaddr.3 ttodata.3 ttosa.3 ttoul.3 +EXTRA_DIST = Android.mk diff --git a/src/libfreeswan/pfkey_v2_parse.c b/src/libfreeswan/pfkey_v2_parse.c index 7c0934c25..8fec9d119 100644 --- a/src/libfreeswan/pfkey_v2_parse.c +++ b/src/libfreeswan/pfkey_v2_parse.c @@ -40,12 +40,11 @@ char pfkey_v2_parse_c_version[] = ""; #define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) -struct satype_tbl { +static struct { uint8_t proto; uint8_t satype; char* name; -} -static satype_tbl[] = { +} satype_tbl[] = { { SA_ESP, SADB_SATYPE_ESP, "ESP" }, { SA_AH, SADB_SATYPE_AH, "AH" }, { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, diff --git a/src/libhydra/Android.mk b/src/libhydra/Android.mk index ccc527f94..075f8dbcb 100644 --- a/src/libhydra/Android.mk +++ b/src/libhydra/Android.mk @@ -32,6 +32,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := libhydra +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c index 922f27094..573557506 100644 --- a/src/libhydra/kernel/kernel_interface.c +++ b/src/libhydra/kernel/kernel_interface.c @@ -340,7 +340,7 @@ METHOD(kernel_interface_t, get_address_by_ts, status_t, if (!found) { - DBG1(DBG_KNL, "no local address found in traffic selector %R", ts); + DBG2(DBG_KNL, "no local address found in traffic selector %R", ts); return FAILED; } diff --git a/src/libhydra/plugins/attr_sql/sql_attribute.c b/src/libhydra/plugins/attr_sql/sql_attribute.c index fe7811b36..714bbcd72 100644 --- a/src/libhydra/plugins/attr_sql/sql_attribute.c +++ b/src/libhydra/plugins/attr_sql/sql_attribute.c @@ -38,7 +38,7 @@ struct private_sql_attribute_t { database_t *db; /** - * wheter to record lease history in lease table + * whether to record lease history in lease table */ bool history; }; diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index 49f5c3378..3451b673f 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -202,7 +202,9 @@ static kernel_algorithm_t encryption_algs[] = { */ static kernel_algorithm_t integrity_algs[] = { {AUTH_HMAC_MD5_96, "md5" }, + {AUTH_HMAC_MD5_128, "hmac(md5)" }, {AUTH_HMAC_SHA1_96, "sha1" }, + {AUTH_HMAC_SHA1_160, "hmac(sha1)" }, {AUTH_HMAC_SHA2_256_96, "sha256" }, {AUTH_HMAC_SHA2_256_128, "hmac(sha256)" }, {AUTH_HMAC_SHA2_384_192, "hmac(sha384)" }, @@ -1279,6 +1281,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (int_alg != AUTH_UNDEFINED) { + u_int trunc_len = 0; + alg_name = lookup_algorithm(integrity_algs, int_alg); if (alg_name == NULL) { @@ -1289,12 +1293,26 @@ METHOD(kernel_ipsec_t, add_sa, status_t, DBG2(DBG_KNL, " using integrity algorithm %N with key size %d", integrity_algorithm_names, int_alg, int_key.len * 8); - if (int_alg == AUTH_HMAC_SHA2_256_128) + switch (int_alg) + { + case AUTH_HMAC_MD5_128: + case AUTH_HMAC_SHA2_256_128: + trunc_len = 128; + break; + case AUTH_HMAC_SHA1_160: + trunc_len = 160; + break; + default: + break; + } + + if (trunc_len) { struct xfrm_algo_auth* algo; /* the kernel uses SHA256 with 96 bit truncation by default, - * use specified truncation size supported by newer kernels */ + * use specified truncation size supported by newer kernels. + * also use this for untruncated MD5 and SHA1. */ rthdr->rta_type = XFRMA_ALG_AUTH_TRUNC; rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_auth) + int_key.len); @@ -1307,7 +1325,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, algo = (struct xfrm_algo_auth*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; - algo->alg_trunc_len = 128; + algo->alg_trunc_len = trunc_len; strcpy(algo->alg_name, alg_name); memcpy(algo->alg_key, int_key.ptr, int_key.len); } @@ -1990,7 +2008,8 @@ METHOD(kernel_ipsec_t, flush_sas, status_t, /** * Add or update a policy in the kernel. * - * Note: The mutex has to be locked when entering this function. + * Note: The mutex has to be locked when entering this function + * and is unlocked here in any case. */ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, policy_entry_t *policy, policy_sa_t *mapping, bool update) @@ -2060,6 +2079,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, hdr->nlmsg_len += RTA_ALIGN(RTA_LENGTH(sizeof(struct xfrm_user_tmpl))); if (hdr->nlmsg_len > sizeof(request)) { + this->mutex->unlock(this->mutex); return FAILED; } @@ -2096,6 +2116,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); if (hdr->nlmsg_len > sizeof(request)) { + this->mutex->unlock(this->mutex); return FAILED; } @@ -2544,6 +2565,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len); if (hdr->nlmsg_len > sizeof(request)) { + this->mutex->unlock(this->mutex); return FAILED; } diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index 219657541..cce0ff402 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -375,9 +375,13 @@ static void process_link(private_kernel_netlink_net_t *this, { if (current->ifindex == msg->ifi_index) { - /* we do not remove it, as an address may be added to a - * "down" interface and we wan't to know that. */ - current->flags = msg->ifi_flags; + if (event) + { + update = TRUE; + DBG1(DBG_KNL, "interface %s deleted", current->ifname); + } + this->ifaces->remove_at(this->ifaces, enumerator); + iface_entry_destroy(current); break; } } @@ -1538,7 +1542,7 @@ kernel_netlink_net_t *kernel_netlink_net_create() return NULL; } addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_LINK; + RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) { DBG1(DBG_KNL, "unable to bind RT event socket"); diff --git a/src/libhydra/plugins/resolve/resolve_plugin.c b/src/libhydra/plugins/resolve/resolve_plugin.c index d23d36127..f95827ed9 100644 --- a/src/libhydra/plugins/resolve/resolve_plugin.c +++ b/src/libhydra/plugins/resolve/resolve_plugin.c @@ -31,7 +31,7 @@ struct private_resolve_plugin_t { resolve_plugin_t public; /** - * The registerd DNS attribute handler + * The registered DNS attribute handler */ resolve_handler_t *handler; }; diff --git a/src/libimcv/Makefile.am b/src/libimcv/Makefile.am index 1b240a1d9..fae9fd662 100644 --- a/src/libimcv/Makefile.am +++ b/src/libimcv/Makefile.am @@ -36,11 +36,3 @@ endif if USE_IMV_SCANNER SUBDIRS += plugins/imv_scanner endif - -if USE_IMC_ATTESTATION - SUBDIRS += plugins/imc_attestation -endif - -if USE_IMV_ATTESTATION - SUBDIRS += plugins/imv_attestation -endif diff --git a/src/libimcv/ietf/ietf_attr_port_filter.c b/src/libimcv/ietf/ietf_attr_port_filter.c index c9b76dde5..b53019657 100644 --- a/src/libimcv/ietf/ietf_attr_port_filter.c +++ b/src/libimcv/ietf/ietf_attr_port_filter.c @@ -81,6 +81,11 @@ struct private_ietf_attr_port_filter_t { * List of Port Filter entries */ linked_list_t *ports; + + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -166,12 +171,22 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_ietf_attr_port_filter_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_ietf_attr_port_filter_t *this) { - this->ports->destroy_function(this->ports, free); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + this->ports->destroy_function(this->ports, free); + free(this->value.ptr); + free(this); + } } METHOD(ietf_attr_port_filter_t, add_port, void, @@ -224,6 +239,7 @@ pa_tnc_attr_t *ietf_attr_port_filter_create(void) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .add_port = _add_port, @@ -232,6 +248,7 @@ pa_tnc_attr_t *ietf_attr_port_filter_create(void) .vendor_id = PEN_IETF, .type = IETF_ATTR_PORT_FILTER, .ports = linked_list_create(), + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -252,6 +269,7 @@ pa_tnc_attr_t *ietf_attr_port_filter_create_from_data(chunk_t data) .get_value = _get_value, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .add_port = _add_port, @@ -261,6 +279,7 @@ pa_tnc_attr_t *ietf_attr_port_filter_create_from_data(chunk_t data) .type = IETF_ATTR_PORT_FILTER, .value = chunk_clone(data), .ports = linked_list_create(), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libimcv/ietf/ietf_attr_product_info.c b/src/libimcv/ietf/ietf_attr_product_info.c index 222fef0bf..548793547 100644 --- a/src/libimcv/ietf/ietf_attr_product_info.c +++ b/src/libimcv/ietf/ietf_attr_product_info.c @@ -80,6 +80,10 @@ struct private_ietf_attr_product_info_t { */ char *product_name; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -154,12 +158,22 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_ietf_attr_product_info_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_ietf_attr_product_info_t *this) { - free(this->product_name); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->product_name); + free(this->value.ptr); + free(this); + } } METHOD(ietf_attr_product_info_t, get_info, char*, @@ -194,6 +208,7 @@ pa_tnc_attr_t *ietf_attr_product_info_create(pen_t vendor_id, u_int16_t id, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_info = _get_info, @@ -203,6 +218,7 @@ pa_tnc_attr_t *ietf_attr_product_info_create(pen_t vendor_id, u_int16_t id, .product_vendor_id = vendor_id, .product_id = id, .product_name = strdup(name), + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -223,6 +239,7 @@ pa_tnc_attr_t *ietf_attr_product_info_create_from_data(chunk_t data) .get_value = _get_value, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_info = _get_info, @@ -230,6 +247,7 @@ pa_tnc_attr_t *ietf_attr_product_info_create_from_data(chunk_t data) .vendor_id = PEN_IETF, .type = IETF_ATTR_PRODUCT_INFORMATION, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libimcv/imc/imc_agent.c b/src/libimcv/imc/imc_agent.c index 82778a5b6..6bba69733 100644 --- a/src/libimcv/imc/imc_agent.c +++ b/src/libimcv/imc/imc_agent.c @@ -39,9 +39,14 @@ struct private_imc_agent_t { const char *name; /** - * message type of IMC + * message vendor ID of IMC */ - TNC_MessageType type; + TNC_VendorID vendor_id; + + /** + * message subtype of IMC + */ + TNC_MessageSubtype subtype; /** * ID of IMC as assigned by TNCC @@ -49,17 +54,22 @@ struct private_imc_agent_t { TNC_IMCID id; /** + * List of additional IMC IDs assigned by TNCC + */ + linked_list_t *additional_ids; + + /** * list of TNCC connection entries */ linked_list_t *connections; /** - * rwlock to lock TNCS connection entries + * rwlock to lock TNCC connection entries */ rwlock_t *connection_lock; /** - * Inform a TNCS about the set of message types the IMC is able to receive + * Inform a TNCC about the set of message types the IMC is able to receive * * @param imc_id IMC ID assigned by TNCC * @param supported_types list of supported message types @@ -71,6 +81,20 @@ struct private_imc_agent_t { TNC_UInt32 type_count); /** + * Inform a TNCC about the set of message types the IMC is able to receive + * + * @param imc_id IMC ID assigned by TNCC + * @param supported_vids list of supported message vendor IDs + * @param supported_subtypes list of supported message subtypes + * @param type_count number of list elements + * @return TNC result code + */ + TNC_Result (*report_message_types_long)(TNC_IMCID imc_id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count); + + /** * Call when an IMC-IMC message is to be sent * * @param imc_id IMC ID assigned by TNCC @@ -85,6 +109,76 @@ struct private_imc_agent_t { TNC_BufferReference msg, TNC_UInt32 msg_len, TNC_MessageType msg_type); + + + /** + * Call when an IMC-IMC message is to be sent with long message types + * + * @param imc_id IMC ID assigned by TNCC + * @param connection_id network connection ID assigned by TNCC + * @param msg_flags message flags + * @param msg message to send + * @param msg_len message length in bytes + * @param msg_vid message vendor ID + * @param msg_subtype message subtype + * @param dst_imc_id destination IMV ID + * @return TNC result code + */ + TNC_Result (*send_message_long)(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 dst_imv_id); + + /** + * Get the value of an attribute associated with a connection + * or with the TNCC as a whole. + * + * @param imc_id IMC ID assigned by TNCC + * @param connection_id network connection ID assigned by TNCC + * @param attribute_id attribute ID + * @param buffer_len length of buffer in bytes + * @param buffer buffer + * @param out_value_len size in bytes of attribute stored in buffer + * @return TNC result code + */ + TNC_Result (*get_attribute)(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_AttributeID attribute_id, + TNC_UInt32 buffer_len, + TNC_BufferReference buffer, + TNC_UInt32 *out_value_len); + + /** + * Set the value of an attribute associated with a connection + * or with the TNCC as a whole. + * + * @param imc_id IMV ID assigned by TNCC + * @param connection_id network connection ID assigned by TNCC + * @param attribute_id attribute ID + * @param buffer_len length of buffer in bytes + * @param buffer buffer + * @return TNC result code + */ + TNC_Result (*set_attribute)(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_AttributeID attribute_id, + TNC_UInt32 buffer_len, + TNC_BufferReference buffer); + + /** + * Reserve an additional IMC ID + * + * @param imc_id primary IMC ID assigned by TNCC + * @param out_imc_id additional IMC ID assigned by TNCC + * @return TNC result code + */ + TNC_Result (*reserve_additional_id)(TNC_IMCID imc_id, + TNC_UInt32 *out_imc_id); + }; METHOD(imc_agent_t, bind_functions, TNC_Result, @@ -100,6 +194,11 @@ METHOD(imc_agent_t, bind_functions, TNC_Result, { this->report_message_types = NULL; } + if (bind_function(this->id, "TNC_TNCC_ReportMessageTypesLong", + (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS) + { + this->report_message_types_long = NULL; + } if (bind_function(this->id, "TNC_TNCC_RequestHandshakeRetry", (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS) { @@ -110,12 +209,42 @@ METHOD(imc_agent_t, bind_functions, TNC_Result, { this->send_message = NULL; } + if (bind_function(this->id, "TNC_TNCC_SendMessageLong", + (void**)&this->send_message_long) != TNC_RESULT_SUCCESS) + { + this->send_message_long = NULL; + } + if (bind_function(this->id, "TNC_TNCC_GetAttribute", + (void**)&this->get_attribute) != TNC_RESULT_SUCCESS) + { + this->get_attribute = NULL; + } + if (bind_function(this->id, "TNC_TNCC_SetAttribute", + (void**)&this->set_attribute) != TNC_RESULT_SUCCESS) + { + this->set_attribute = NULL; + } + if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMCID", + (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS) + { + this->reserve_additional_id = NULL; + } DBG2(DBG_IMC, "IMC %u \"%s\" provided with bind function", this->id, this->name); - if (this->report_message_types) + if (this->report_message_types_long) + { + this->report_message_types_long(this->id, &this->vendor_id, + &this->subtype, 1); + } + else if (this->report_message_types && + this->vendor_id <= TNC_VENDORID_ANY && + this->subtype <= TNC_SUBTYPE_ANY) { - this->report_message_types(this->id, &this->type, 1); + TNC_MessageType type; + + type = (this->vendor_id << 8) | this->subtype; + this->report_message_types(this->id, &type, 1); } return TNC_RESULT_SUCCESS; } @@ -172,24 +301,78 @@ static bool delete_connection(private_imc_agent_t *this, TNC_ConnectionID id) return found; } +/** + * Read a boolean attribute + */ +static bool get_bool_attribute(private_imc_agent_t *this, TNC_ConnectionID id, + TNC_AttributeID attribute_id) +{ + TNC_UInt32 len; + char buf[4]; + + return this->get_attribute && + this->get_attribute(this->id, id, attribute_id, 4, buf, &len) == + TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01; + } + +/** + * Read a string attribute + */ +static char* get_str_attribute(private_imc_agent_t *this, TNC_ConnectionID id, + TNC_AttributeID attribute_id) +{ + TNC_UInt32 len; + char buf[BUF_LEN]; + + if (this->get_attribute && + this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) == + TNC_RESULT_SUCCESS && len <= BUF_LEN) + { + return strdup(buf); + } + return NULL; + } + METHOD(imc_agent_t, create_state, TNC_Result, private_imc_agent_t *this, imc_state_t *state) { - TNC_ConnectionID connection_id; + 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; - connection_id = state->get_connection_id(state); - if (find_connection(this, connection_id)) + conn_id = state->get_connection_id(state); + if (find_connection(this, conn_id)) { DBG1(DBG_IMC, "IMC %u \"%s\" already created a state for Connection ID %u", - this->id, this->name, connection_id); + this->id, this->name, conn_id); state->destroy(state); return TNC_RESULT_OTHER; } + + /* Get and display attributes from TNCC via IF-IMC */ + has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES); + has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE); + has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH); + tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL); + tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION); + t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL); + t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION); + + state->set_flags(state, has_long, has_excl); + + DBG2(DBG_IMC, "IMC %u \"%s\" created a state for Connection ID %u: " + "%s %s with %slong %sexcl %ssoh over %s %s", + this->id, this->name, conn_id, tnccs_p ? tnccs_p:"?", + tnccs_v ? tnccs_v:"?", has_long ? "+":"-", has_excl ? "+":"-", + has_soh ? "+":"-", t_p ? t_p:"?", t_v ? t_v :"?"); + free(tnccs_p); + free(tnccs_v); + free(t_p); + free(t_v); + this->connection_lock->write_lock(this->connection_lock); this->connections->insert_last(this->connections, state); this->connection_lock->unlock(this->connection_lock); - DBG2(DBG_IMC, "IMC %u \"%s\" created a state for Connection ID %u", - this->id, this->name, connection_id); return TNC_RESULT_SUCCESS; } @@ -269,27 +452,78 @@ METHOD(imc_agent_t, get_state, bool, } METHOD(imc_agent_t, send_message, TNC_Result, - private_imc_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg) + private_imc_agent_t *this, TNC_ConnectionID connection_id, bool excl, + TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id, chunk_t msg) { - if (!this->send_message) + TNC_MessageType type; + TNC_UInt32 msg_flags; + imc_state_t *state; + + state = find_connection(this, connection_id); + if (!state) { + DBG1(DBG_IMV, "IMC %u \"%s\" has no state for Connection ID %u", + this->id, this->name, connection_id); return TNC_RESULT_FATAL; } - return this->send_message(this->id, connection_id, msg.ptr, msg.len, - this->type); + + if (state->has_long(state) && this->send_message_long) + { + if (!src_imc_id) + { + src_imc_id = this->id; + } + msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0; + + return this->send_message_long(src_imc_id, connection_id, msg_flags, + msg.ptr, msg.len, this->vendor_id, + this->subtype, dst_imv_id); + } + if (this->send_message) + { + type = (this->vendor_id << 8) | this->subtype; + + return this->send_message(this->id, connection_id, msg.ptr, msg.len, + type); + } + return TNC_RESULT_FATAL; } METHOD(imc_agent_t, receive_message, TNC_Result, - private_imc_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg, - TNC_MessageType msg_type, pa_tnc_msg_t **pa_tnc_msg) + private_imc_agent_t *this, imc_state_t *state, chunk_t msg, + TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, TNC_UInt32 dst_imc_id, pa_tnc_msg_t **pa_tnc_msg) { pa_tnc_msg_t *pa_msg, *error_msg; pa_tnc_attr_t *error_attr; enumerator_t *enumerator; + TNC_MessageType msg_type; + TNC_UInt32 msg_flags, src_imc_id, dst_imv_id; + TNC_ConnectionID connection_id; TNC_Result result; - DBG2(DBG_IMV, "IMC %u \"%s\" received message type 0x%08x for Connection ID %u", - this->id, this->name, msg_type, connection_id); + connection_id = state->get_connection_id(state); + + if (state->has_long(state)) + { + if (dst_imc_id != TNC_IMCID_ANY) + { + DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u " + "from IMV %u to IMC %u", this->id, this->name, + connection_id, src_imv_id, dst_imc_id); + } + else + { + DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u " + "from IMV %u", this->id, this->name, connection_id, + src_imv_id); + } + } + else + { + DBG2(DBG_IMC, "IMC %u \"%s\" received message for Connection ID %u", + this->id, this->name, connection_id); + } *pa_tnc_msg = NULL; pa_msg = pa_tnc_msg_create_from_data(msg); @@ -300,12 +534,6 @@ METHOD(imc_agent_t, receive_message, TNC_Result, *pa_tnc_msg = pa_msg; break; case VERIFY_ERROR: - if (!this->send_message) - { - /* TNCC doen't have a SendMessage() function */ - return TNC_RESULT_FATAL; - } - /* build error message */ error_msg = pa_tnc_msg_create(); enumerator = pa_msg->create_error_enumerator(pa_msg); @@ -318,9 +546,36 @@ METHOD(imc_agent_t, receive_message, TNC_Result, error_msg->build(error_msg); /* send error message */ - msg = error_msg->get_encoding(error_msg); - result = this->send_message(this->id, connection_id, + if (state->has_long(state) && this->send_message_long) + { + if (state->has_excl(state)) + { + msg_flags = TNC_MESSAGE_FLAGS_EXCLUSIVE; + dst_imv_id = src_imv_id; + } + else + { + msg_flags = 0; + dst_imv_id = TNC_IMVID_ANY; + } + src_imc_id = (dst_imc_id == TNC_IMCID_ANY) ? this->id + : dst_imc_id; + + result = this->send_message_long(src_imc_id, connection_id, + msg_flags, msg.ptr, msg.len, msg_vid, + msg_subtype, dst_imv_id); + } + else if (this->send_message) + { + msg_type = (msg_vid << 8) | msg_subtype; + + result = this->send_message(this->id, connection_id, msg.ptr, msg.len, msg_type); + } + else + { + result = TNC_RESULT_FATAL; + } /* clean up */ error_msg->destroy(error_msg); @@ -334,10 +589,56 @@ METHOD(imc_agent_t, receive_message, TNC_Result, return TNC_RESULT_SUCCESS; } +METHOD(imc_agent_t, reserve_additional_ids, TNC_Result, + private_imc_agent_t *this, int count) +{ + TNC_Result result; + TNC_UInt32 id; + void *pointer; + + if (!this->reserve_additional_id) + { + DBG1(DBG_IMC, "IMC %u \"%s\" did not detect the capability to reserve " + "additional IMC IDs from the TNCC", this->id, this->name); + return TNC_RESULT_ILLEGAL_OPERATION; + } + while (count > 0) + { + result = this->reserve_additional_id(this->id, &id); + if (result != TNC_RESULT_SUCCESS) + { + DBG1(DBG_IMC, "IMC %u \"%s\" failed to reserve %d additional IMC IDs", + this->id, this->name, count); + return result; + } + count--; + + /* store the scalar value in the pointer */ + pointer = (void*)id; + this->additional_ids->insert_last(this->additional_ids, pointer); + DBG2(DBG_IMC, "IMC %u \"%s\" reserved additional ID %u", + this->id, this->name, id); + } + return TNC_RESULT_SUCCESS; +} + +METHOD(imc_agent_t, count_additional_ids, int, + private_imc_agent_t *this) +{ + return this->additional_ids->get_count(this->additional_ids); +} + +METHOD(imc_agent_t, create_id_enumerator, enumerator_t*, + private_imc_agent_t *this) +{ + return this->additional_ids->create_enumerator(this->additional_ids); +} + METHOD(imc_agent_t, destroy, void, private_imc_agent_t *this) { DBG1(DBG_IMC, "IMC %u \"%s\" terminated", this->id, this->name); + this->additional_ids->destroy(this->additional_ids); this->connections->destroy_function(this->connections, free); this->connection_lock->destroy(this->connection_lock); free(this); @@ -370,11 +671,16 @@ imc_agent_t *imc_agent_create(const char *name, .get_state = _get_state, .send_message = _send_message, .receive_message = _receive_message, + .reserve_additional_ids = _reserve_additional_ids, + .count_additional_ids = _count_additional_ids, + .create_id_enumerator = _create_id_enumerator, .destroy = _destroy, }, .name = name, - .type = (vendor_id << 8) | (subtype & 0xff), + .vendor_id = vendor_id, + .subtype = subtype, .id = id, + .additional_ids = linked_list_create(), .connections = linked_list_create(), .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); diff --git a/src/libimcv/imc/imc_agent.h b/src/libimcv/imc/imc_agent.h index 1912a39c1..d1fef4d8d 100644 --- a/src/libimcv/imc/imc_agent.h +++ b/src/libimcv/imc/imc_agent.h @@ -101,28 +101,58 @@ struct imc_agent_t { * Call when an PA-TNC message is to be sent * * @param connection_id network connection ID assigned by TNCC + * @param excl exclusive flag + * @param src_imc_id IMC ID to be set as source + * @param dst_imv_id IMV ID to be set as destination * @param msg message to send * @return TNC result code */ TNC_Result (*send_message)(imc_agent_t *this, - TNC_ConnectionID connection_id, + TNC_ConnectionID connection_id, bool excl, + TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id, chunk_t msg); /** * Call when a PA-TNC message was received * - * @param connection_id network connection ID assigned by TNCC + * @param state state for current connection * @param msg received unparsed message - * @param msg_type message type of the received message + * @param msg_vid message vendorID of the received message + * @param msg_subtype message subtype of the received message + * @param src_imv_id source IMV ID + * @param dst_imc_id destination IMC ID * @param pa_tnc_message parsed PA-TNC message or NULL if an error occurred * @return TNC result code */ TNC_Result (*receive_message)(imc_agent_t *this, - TNC_ConnectionID connection_id, chunk_t msg, - TNC_MessageType msg_type, + imc_state_t *state, chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id, pa_tnc_msg_t **pa_tnc_msg); /** + * Reserve additional IMC IDs from TNCC + * + * @param count number of additional IMC IDs to be assigned + * @return TNC result code + */ + TNC_Result (*reserve_additional_ids)(imc_agent_t *this, int count); + + /** + * Return the number of additional IMC IDs assigned by the TNCC + * + * @return number of additional IMC IDs + */ + int (*count_additional_ids)(imc_agent_t *this); + + /** + * Create an enumerator for the additional IMC IDs + */ + enumerator_t* (*create_id_enumerator)(imc_agent_t *this); + + /** * Destroys an imc_agent_t object */ void (*destroy)(imc_agent_t *this); diff --git a/src/libimcv/imc/imc_state.h b/src/libimcv/imc/imc_state.h index 73013f8ce..f1b0358c9 100644 --- a/src/libimcv/imc/imc_state.h +++ b/src/libimcv/imc/imc_state.h @@ -33,13 +33,37 @@ typedef struct imc_state_t imc_state_t; struct imc_state_t { /** - * Get the TNCS connection ID attached to the state + * Get the TNCS connection I +D attached to the state * * @return TNCS connection ID of the state */ TNC_ConnectionID (*get_connection_id)(imc_state_t *this); /** + * Checks if long message types are supported for this TNCCS connection + * + * @return TRUE if set, FALSE otherwise + */ + bool (*has_long)(imc_state_t *this); + + /** + * Checks if the exclusive delivery is supported for this TNCCS connection + * + * @return TRUE if set, FALSE otherwise + */ + bool (*has_excl)(imc_state_t *this); + + /** + * Sets the long message types and exclusive flags for this TNCCS connection + * + * @param has_long TNCCS connection supports long message types + * @param has_excl TNCCS connection supports exclusive delivery + * @return TRUE if set, FALSE otherwise + */ + void (*set_flags)(imc_state_t *this, bool has_long, bool has_excl); + + /** * Change the connection state * * @param new_state new connection state diff --git a/src/libimcv/imv/imv_agent.c b/src/libimcv/imv/imv_agent.c index 290bb1147..56131c547 100644 --- a/src/libimcv/imv/imv_agent.c +++ b/src/libimcv/imv/imv_agent.c @@ -39,9 +39,14 @@ struct private_imv_agent_t { const char *name; /** - * message type of IMV + * message vendor ID of IMV */ - TNC_MessageType type; + TNC_VendorID vendor_id; + + /** + * message subtype of IMV + */ + TNC_MessageSubtype subtype; /** * ID of IMV as assigned by TNCS @@ -49,6 +54,11 @@ struct private_imv_agent_t { TNC_IMVID id; /** + * List of additional IMV IDs assigned by TNCS + */ + linked_list_t *additional_ids; + + /** * list of TNCS connection entries */ linked_list_t *connections; @@ -59,7 +69,7 @@ struct private_imv_agent_t { rwlock_t *connection_lock; /** - * Inform a TNCS about the set of message types the IMV is able to receive + * Inform a TNCS about the set of message types the IMV is able to receive * * @param imv_id IMV ID assigned by TNCS * @param supported_types list of supported message types @@ -71,6 +81,20 @@ struct private_imv_agent_t { TNC_UInt32 type_count); /** + * Inform a TNCS about the set of message types the IMV is able to receive + * + * @param imv_id IMV ID assigned by TNCS + * @param supported_vids list of supported message vendor IDs + * @param supported_subtypes list of supported message subtypes + * @param type_count number of list elements + * @return TNC result code + */ + TNC_Result (*report_message_types_long)(TNC_IMVID imv_id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count); + + /** * Call when an IMV-IMC message is to be sent * * @param imv_id IMV ID assigned by TNCS @@ -87,6 +111,28 @@ struct private_imv_agent_t { TNC_MessageType msg_type); /** + * Call when an IMV-IMC message is to be sent with long message types + * + * @param imv_id IMV ID assigned by TNCS + * @param connection_id network connection ID assigned by TNCS + * @param msg_flags message flags + * @param msg message to send + * @param msg_len message length in bytes + * @param msg_vid message vendor ID + * @param msg_subtype message subtype + * @param dst_imc_id destination IMC ID + * @return TNC result code + */ + TNC_Result (*send_message_long)(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 dst_imc_id); + + /** * Deliver IMV Action Recommendation and IMV Evaluation Results to the TNCS * * @param imv_id IMV ID assigned by TNCS @@ -135,6 +181,17 @@ struct private_imv_agent_t { TNC_AttributeID attribute_id, TNC_UInt32 buffer_len, TNC_BufferReference buffer); + + /** + * Reserve an additional IMV ID + * + * @param imv_id primary IMV ID assigned by TNCS + * @param out_imv_id additional IMV ID assigned by TNCS + * @return TNC result code + */ + TNC_Result (*reserve_additional_id)(TNC_IMVID imv_id, + TNC_UInt32 *out_imv_id); + }; METHOD(imv_agent_t, bind_functions, TNC_Result, @@ -150,6 +207,11 @@ METHOD(imv_agent_t, bind_functions, TNC_Result, { this->report_message_types = NULL; } + if (bind_function(this->id, "TNC_TNCS_ReportMessageTypesLong", + (void**)&this->report_message_types_long) != TNC_RESULT_SUCCESS) + { + this->report_message_types_long = NULL; + } if (bind_function(this->id, "TNC_TNCS_RequestHandshakeRetry", (void**)&this->public.request_handshake_retry) != TNC_RESULT_SUCCESS) { @@ -160,6 +222,11 @@ METHOD(imv_agent_t, bind_functions, TNC_Result, { this->send_message = NULL; } + if (bind_function(this->id, "TNC_TNCS_SendMessageLong", + (void**)&this->send_message_long) != TNC_RESULT_SUCCESS) + { + this->send_message_long = NULL; + } if (bind_function(this->id, "TNC_TNCS_ProvideRecommendation", (void**)&this->provide_recommendation) != TNC_RESULT_SUCCESS) { @@ -175,12 +242,27 @@ METHOD(imv_agent_t, bind_functions, TNC_Result, { this->set_attribute = NULL; } + if (bind_function(this->id, "TNC_TNCC_ReserveAdditionalIMVID", + (void**)&this->reserve_additional_id) != TNC_RESULT_SUCCESS) + { + this->reserve_additional_id = NULL; + } DBG2(DBG_IMV, "IMV %u \"%s\" provided with bind function", this->id, this->name); - if (this->report_message_types) + if (this->report_message_types_long) + { + this->report_message_types_long(this->id, &this->vendor_id, + &this->subtype, 1); + } + else if (this->report_message_types && + this->vendor_id <= TNC_VENDORID_ANY && + this->subtype <= TNC_SUBTYPE_ANY) { - this->report_message_types(this->id, &this->type, 1); + TNC_MessageType type; + + type = (this->vendor_id << 8) | this->subtype; + this->report_message_types(this->id, &type, 1); } return TNC_RESULT_SUCCESS; } @@ -237,24 +319,78 @@ static bool delete_connection(private_imv_agent_t *this, TNC_ConnectionID id) return found; } +/** + * Read a boolean attribute + */ +static bool get_bool_attribute(private_imv_agent_t *this, TNC_ConnectionID id, + TNC_AttributeID attribute_id) +{ + TNC_UInt32 len; + char buf[4]; + + return this->get_attribute && + this->get_attribute(this->id, id, attribute_id, 4, buf, &len) == + TNC_RESULT_SUCCESS && len == 1 && *buf == 0x01; + } + +/** + * Read a string attribute + */ +static char* get_str_attribute(private_imv_agent_t *this, TNC_ConnectionID id, + TNC_AttributeID attribute_id) +{ + TNC_UInt32 len; + char buf[BUF_LEN]; + + if (this->get_attribute && + this->get_attribute(this->id, id, attribute_id, BUF_LEN, buf, &len) == + TNC_RESULT_SUCCESS && len <= BUF_LEN) + { + return strdup(buf); + } + return NULL; + } + METHOD(imv_agent_t, create_state, TNC_Result, private_imv_agent_t *this, imv_state_t *state) { - TNC_ConnectionID connection_id; + 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; - connection_id = state->get_connection_id(state); - if (find_connection(this, connection_id)) + conn_id = state->get_connection_id(state); + if (find_connection(this, conn_id)) { DBG1(DBG_IMV, "IMV %u \"%s\" already created a state for Connection ID %u", - this->id, this->name, connection_id); + this->id, this->name, conn_id); state->destroy(state); return TNC_RESULT_OTHER; } + + /* Get and display attributes from TNCS via IF-IMV */ + has_long = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_LONG_TYPES); + has_excl = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_EXCLUSIVE); + has_soh = get_bool_attribute(this, conn_id, TNC_ATTRIBUTEID_HAS_SOH); + tnccs_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL); + tnccs_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFTNCCS_VERSION); + t_p = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_PROTOCOL); + t_v = get_str_attribute(this, conn_id, TNC_ATTRIBUTEID_IFT_VERSION); + + state->set_flags(state, has_long, has_excl); + + DBG2(DBG_IMV, "IMV %u \"%s\" created a state for Connection ID %u: " + "%s %s with %slong %sexcl %ssoh over %s %s", + this->id, this->name, conn_id, tnccs_p ? tnccs_p:"?", + tnccs_v ? tnccs_v:"?", has_long ? "+":"-", has_excl ? "+":"-", + has_soh ? "+":"-", t_p ? t_p:"?", t_v ? t_v :"?"); + free(tnccs_p); + free(tnccs_v); + free(t_p); + free(t_v); + this->connection_lock->write_lock(this->connection_lock); this->connections->insert_last(this->connections, state); this->connection_lock->unlock(this->connection_lock); - DBG2(DBG_IMV, "IMV %u \"%s\" created a state for Connection ID %u", - this->id, this->name, connection_id); return TNC_RESULT_SUCCESS; } @@ -333,14 +469,41 @@ METHOD(imv_agent_t, get_state, bool, } METHOD(imv_agent_t, send_message, TNC_Result, - private_imv_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg) + private_imv_agent_t *this, TNC_ConnectionID connection_id, bool excl, + TNC_UInt32 src_imv_id, TNC_UInt32 dst_imc_id, chunk_t msg) { - if (!this->send_message) + TNC_MessageType type; + TNC_UInt32 msg_flags; + imv_state_t *state; + + state = find_connection(this, connection_id); + if (!state) { + DBG1(DBG_IMV, "IMV %u \"%s\" has no state for Connection ID %u", + this->id, this->name, connection_id); return TNC_RESULT_FATAL; } - return this->send_message(this->id, connection_id, msg.ptr, msg.len, - this->type); + + if (state->has_long(state) && this->send_message_long) + { + if (!src_imv_id) + { + src_imv_id = this->id; + } + msg_flags = excl ? TNC_MESSAGE_FLAGS_EXCLUSIVE : 0; + + return this->send_message_long(src_imv_id, connection_id, msg_flags, + msg.ptr, msg.len, this->vendor_id, + this->subtype, dst_imc_id); + } + if (this->send_message) + { + type = (this->vendor_id << 8) | this->subtype; + + return this->send_message(this->id, connection_id, msg.ptr, msg.len, + type); + } + return TNC_RESULT_FATAL; } METHOD(imv_agent_t, set_recommendation, TNC_Result, @@ -363,16 +526,40 @@ METHOD(imv_agent_t, set_recommendation, TNC_Result, } METHOD(imv_agent_t, receive_message, TNC_Result, - private_imv_agent_t *this, TNC_ConnectionID connection_id, chunk_t msg, - TNC_MessageType msg_type, pa_tnc_msg_t **pa_tnc_msg) + private_imv_agent_t *this, imv_state_t *state, chunk_t msg, + TNC_VendorID msg_vid, TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, TNC_UInt32 dst_imv_id, pa_tnc_msg_t **pa_tnc_msg) { pa_tnc_msg_t *pa_msg, *error_msg; pa_tnc_attr_t *error_attr; enumerator_t *enumerator; + TNC_MessageType msg_type; + TNC_UInt32 msg_flags, src_imv_id, dst_imc_id; + TNC_ConnectionID connection_id; TNC_Result result; - DBG2(DBG_IMV, "IMV %u \"%s\" received message type 0x%08x for Connection ID %u", - this->id, this->name, msg_type, connection_id); + connection_id = state->get_connection_id(state); + + if (state->has_long(state)) + { + if (dst_imv_id != TNC_IMVID_ANY) + { + DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u " + "from IMC %u to IMV %u", this->id, this->name, + connection_id, src_imc_id, dst_imv_id); + } + else + { + DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u " + "from IMC %u", this->id, this->name, connection_id, + src_imc_id); + } + } + else + { + DBG2(DBG_IMV, "IMV %u \"%s\" received message for Connection ID %u", + this->id, this->name, connection_id); + } *pa_tnc_msg = NULL; pa_msg = pa_tnc_msg_create_from_data(msg); @@ -383,12 +570,6 @@ METHOD(imv_agent_t, receive_message, TNC_Result, *pa_tnc_msg = pa_msg; break; case VERIFY_ERROR: - if (!this->send_message) - { - /* TNCS doen't have a SendMessage() function */ - return TNC_RESULT_FATAL; - } - /* build error message */ error_msg = pa_tnc_msg_create(); enumerator = pa_msg->create_error_enumerator(pa_msg); @@ -402,8 +583,37 @@ METHOD(imv_agent_t, receive_message, TNC_Result, /* send error message */ msg = error_msg->get_encoding(error_msg); - result = this->send_message(this->id, connection_id, + + if (state->has_long(state) && this->send_message_long) + { + if (state->has_excl(state)) + { + msg_flags = TNC_MESSAGE_FLAGS_EXCLUSIVE; + dst_imc_id = src_imc_id; + } + else + { + msg_flags = 0; + dst_imc_id = TNC_IMCID_ANY; + } + src_imv_id = (dst_imv_id == TNC_IMVID_ANY) ? this->id + : dst_imv_id; + + result = this->send_message_long(src_imv_id, connection_id, + msg_flags, msg.ptr, msg.len, msg_vid, + msg_subtype, dst_imc_id); + } + else if (this->send_message) + { + msg_type = (msg_vid << 8) | msg_subtype; + + result = this->send_message(this->id, connection_id, msg.ptr, msg.len, msg_type); + } + else + { + result = TNC_RESULT_FATAL; + } /* clean up */ error_msg->destroy(error_msg); @@ -412,7 +622,10 @@ METHOD(imv_agent_t, receive_message, TNC_Result, case FAILED: default: pa_msg->destroy(pa_msg); - return set_recommendation(this, connection_id, + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, + TNC_IMV_EVALUATION_RESULT_ERROR); + return this->provide_recommendation(this->id, connection_id, TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, TNC_IMV_EVALUATION_RESULT_ERROR); } @@ -471,10 +684,56 @@ METHOD(imv_agent_t, provide_recommendation, TNC_Result, return this->provide_recommendation(this->id, connection_id, rec, eval); } +METHOD(imv_agent_t, reserve_additional_ids, TNC_Result, + private_imv_agent_t *this, int count) +{ + TNC_Result result; + TNC_UInt32 id; + void *pointer; + + if (!this->reserve_additional_id) + { + DBG1(DBG_IMV, "IMV %u \"%s\" did not detect the capability to reserve " + "additional IMV IDs from the TNCS", this->id, this->name); + return TNC_RESULT_ILLEGAL_OPERATION; + } + while (count > 0) + { + result = this->reserve_additional_id(this->id, &id); + if (result != TNC_RESULT_SUCCESS) + { + DBG1(DBG_IMV, "IMV %u \"%s\" failed to reserve %d additional IMV IDs", + this->id, this->name, count); + return result; + } + count--; + + /* store the scalar value in the pointer */ + pointer = (void*)id; + this->additional_ids->insert_last(this->additional_ids, pointer); + DBG2(DBG_IMV, "IMV %u \"%s\" reserved additional ID %u", + this->id, this->name, id); + } + return TNC_RESULT_SUCCESS; +} + +METHOD(imv_agent_t, count_additional_ids, int, + private_imv_agent_t *this) +{ + return this->additional_ids->get_count(this->additional_ids); +} + +METHOD(imv_agent_t, create_id_enumerator, enumerator_t*, + private_imv_agent_t *this) +{ + return this->additional_ids->create_enumerator(this->additional_ids); +} + METHOD(imv_agent_t, destroy, void, private_imv_agent_t *this) { DBG1(DBG_IMV, "IMV %u \"%s\" terminated", this->id, this->name); + this->additional_ids->destroy(this->additional_ids); this->connections->destroy_offset(this->connections, offsetof(imv_state_t, destroy)); this->connection_lock->destroy(this->connection_lock); @@ -510,11 +769,16 @@ imv_agent_t *imv_agent_create(const char *name, .receive_message = _receive_message, .set_recommendation = _set_recommendation, .provide_recommendation = _provide_recommendation, + .reserve_additional_ids = _reserve_additional_ids, + .count_additional_ids = _count_additional_ids, + .create_id_enumerator = _create_id_enumerator, .destroy = _destroy, }, .name = name, - .type = (vendor_id << 8) | (subtype & 0xff), + .vendor_id = vendor_id, + .subtype = subtype, .id = id, + .additional_ids = linked_list_create(), .connections = linked_list_create(), .connection_lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); diff --git a/src/libimcv/imv/imv_agent.h b/src/libimcv/imv/imv_agent.h index b6c8841f2..de70f3bc1 100644 --- a/src/libimcv/imv/imv_agent.h +++ b/src/libimcv/imv/imv_agent.h @@ -101,24 +101,35 @@ struct imv_agent_t { * Call when a PA-TNC message is to be sent * * @param connection_id network connection ID assigned by TNCS + * @param excl exclusive flag + * @param src_imv_id IMV ID to be set as source + * @param dst_imc_id IMD ID to be set as destination * @param msg message to send * @return TNC result code */ TNC_Result (*send_message)(imv_agent_t *this, - TNC_ConnectionID connection_id, chunk_t msg); + TNC_ConnectionID connection_id, bool excl, + TNC_UInt32 src_imv_id, TNC_UInt32 dst_imc_id, + chunk_t msg); /** * Call when a PA-TNC message was received * - * @param connection_id network connection ID assigned by TNCS + * @param state state for current connection * @param msg received unparsed message - * @param msg_type message type of the received message + * @param msg_vid message vendorID of the received message + * @param msg_subtype message subtype of the received message + * @param src_imc_id source IMC ID + * @param dst_imv_id destination IMV ID * @param pa_tnc_message parsed PA-TNC message or NULL if an error occurred * @return TNC result code */ TNC_Result (*receive_message)(imv_agent_t *this, - TNC_ConnectionID connection_id, chunk_t msg, - TNC_MessageType msg_type, + imv_state_t *state, chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id, pa_tnc_msg_t **pa_tnc_msg); /** @@ -144,6 +155,26 @@ struct imv_agent_t { TNC_ConnectionID connection_id); /** + * Reserve additional IMV IDs from TNCS + * + * @param count number of additional IMV IDs to be assigned + * @return TNC result code + */ + TNC_Result (*reserve_additional_ids)(imv_agent_t *this, int count); + + /** + * Return the number of additional IMV IDs assigned by the TNCS + * + * @return number of additional IMV IDs + */ + int (*count_additional_ids)(imv_agent_t *this); + + /** + * Create an enumerator for the additional IMV IDs + */ + enumerator_t* (*create_id_enumerator)(imv_agent_t *this); + + /** * Destroys an imv_agent_t object */ void (*destroy)(imv_agent_t *this); diff --git a/src/libimcv/imv/imv_state.h b/src/libimcv/imv/imv_state.h index 26d07bb02..9e7a29a9f 100644 --- a/src/libimcv/imv/imv_state.h +++ b/src/libimcv/imv/imv_state.h @@ -40,6 +40,29 @@ struct imv_state_t { TNC_ConnectionID (*get_connection_id)(imv_state_t *this); /** + * Checks if long message types are supported for this TNCCS connection + * + * @return TRUE if set, FALSE otherwise + */ + bool (*has_long)(imv_state_t *this); + + /** + * Checks if the exclusive delivery is supported for this TNCCS connection + * + * @return TRUE if set, FALSE otherwise + */ + bool (*has_excl)(imv_state_t *this); + + /** + * Sets the long message types and exclusive flags for this TNCCS connection + * + * @param has_long TNCCS connection supports long message types + * @param has_excl TNCCS connection supports exclusive delivery + * @return TRUE if set, FALSE otherwise + */ + void (*set_flags)(imv_state_t *this, bool has_long, bool has_excl); + + /** * Change the connection state * * @param new_state new connection state diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.c b/src/libimcv/pa_tnc/pa_tnc_msg.c index f8d3b9d0e..b5df0a5b5 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.c +++ b/src/libimcv/pa_tnc/pa_tnc_msg.c @@ -311,6 +311,63 @@ err: return VERIFY_ERROR; } +METHOD(pa_tnc_msg_t, process_ietf_std_errors, bool, + private_pa_tnc_msg_t *this) +{ + enumerator_t *enumerator; + pa_tnc_attr_t *attr; + bool fatal_error = FALSE; + + enumerator = this->attributes->create_enumerator(this->attributes); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->get_vendor_id(attr) == PEN_IETF && + attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) + { + ietf_attr_pa_tnc_error_t *error_attr; + pen_t error_vendor_id; + pa_tnc_error_code_t error_code; + chunk_t msg_info, attr_info; + u_int32_t offset; + + error_attr = (ietf_attr_pa_tnc_error_t*)attr; + error_vendor_id = error_attr->get_vendor_id(error_attr); + error_code = error_attr->get_error_code(error_attr); + msg_info = error_attr->get_msg_info(error_attr); + + /* skip errors from non-IETF namespaces */ + if (error_vendor_id != PEN_IETF) + { + continue; + } + DBG1(DBG_IMC, "received PA-TNC error '%N' concerning message " + "0x%08x/0x%08x", pa_tnc_error_code_names, error_code, + untoh32(msg_info.ptr), untoh32(msg_info.ptr + 4)); + + switch (error_code) + { + case PA_ERROR_INVALID_PARAMETER: + offset = error_attr->get_offset(error_attr); + DBG1(DBG_IMC, " occurred at offset of %u bytes", offset); + break; + case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: + attr_info = error_attr->get_attr_info(error_attr); + DBG1(DBG_IMC, " unsupported attribute %#B", &attr_info); + break; + default: + break; + } + + /* remove the processed IETF standard error attribute */ + this->attributes->remove_at(this->attributes, enumerator); + fatal_error = TRUE; + } + } + enumerator->destroy(enumerator); + + return fatal_error; +} + METHOD(pa_tnc_msg_t, create_attribute_enumerator, enumerator_t*, private_pa_tnc_msg_t *this) { @@ -347,6 +404,7 @@ pa_tnc_msg_t *pa_tnc_msg_create_from_data(chunk_t data) .add_attribute = _add_attribute, .build = _build, .process = _process, + .process_ietf_std_errors = _process_ietf_std_errors, .create_attribute_enumerator = _create_attribute_enumerator, .create_error_enumerator = _create_error_enumerator, .destroy = _destroy, diff --git a/src/libimcv/pa_tnc/pa_tnc_msg.h b/src/libimcv/pa_tnc/pa_tnc_msg.h index bff954678..c3ce829d5 100644 --- a/src/libimcv/pa_tnc/pa_tnc_msg.h +++ b/src/libimcv/pa_tnc/pa_tnc_msg.h @@ -62,6 +62,13 @@ struct pa_tnc_msg_t { status_t (*process)(pa_tnc_msg_t *this); /** + * Process and remove all IETF standard error PA-TNC attributes + * + * @return TRUE if at least one error attribute processed + */ + bool (*process_ietf_std_errors)(pa_tnc_msg_t *this); + + /** * Enumerates over all PA-TNC attributes * * @return return attribute enumerator diff --git a/src/libimcv/plugins/imc_attestation/imc_attestation.c b/src/libimcv/plugins/imc_attestation/imc_attestation.c deleted file mode 100644 index 3c26f9b5c..000000000 --- a/src/libimcv/plugins/imc_attestation/imc_attestation.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (C) 2011 Sansar Choinyambuu - * 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 "imc_attestation_state.h" - -#include <imc/imc_agent.h> -#include <pa_tnc/pa_tnc_msg.h> -#include <ietf/ietf_attr.h> -#include <ietf/ietf_attr_pa_tnc_error.h> -#include <ietf/ietf_attr_product_info.h> - -#include <libpts.h> - -#include <pts/pts_error.h> - -#include <tcg/tcg_pts_attr_proto_caps.h> -#include <tcg/tcg_pts_attr_meas_algo.h> -#include <tcg/tcg_pts_attr_get_tpm_version_info.h> -#include <tcg/tcg_pts_attr_tpm_version_info.h> -#include <tcg/tcg_pts_attr_get_aik.h> -#include <tcg/tcg_pts_attr_aik.h> -#include <tcg/tcg_pts_attr_req_funct_comp_evid.h> -#include <tcg/tcg_pts_attr_gen_attest_evid.h> -#include <tcg/tcg_pts_attr_simple_comp_evid.h> -#include <tcg/tcg_pts_attr_simple_evid_final.h> -#include <tcg/tcg_pts_attr_req_file_meas.h> -#include <tcg/tcg_pts_attr_file_meas.h> - -#include <tncif_pa_subtypes.h> - -#include <pen/pen.h> -#include <debug.h> -#include <utils/linked_list.h> - -/* IMC definitions */ - -static const char imc_name[] = "Attestation"; - -#define IMC_VENDOR_ID PEN_TCG -#define IMC_SUBTYPE PA_SUBTYPE_TCG_PTS - -static imc_agent_t *imc_attestation; - -/** - * Supported PTS measurement algorithms - */ -static pts_meas_algorithms_t supported_algorithms = 0; - -/** - * see section 3.7.1 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id, - TNC_Version min_version, - TNC_Version max_version, - TNC_Version *actual_version) -{ - if (imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name); - return TNC_RESULT_ALREADY_INITIALIZED; - } - if (!pts_meas_probe_algorithms(&supported_algorithms)) - { - return TNC_RESULT_FATAL; - } - imc_attestation = imc_agent_create(imc_name, IMC_VENDOR_ID, IMC_SUBTYPE, - imc_id, actual_version); - if (!imc_attestation) - { - return TNC_RESULT_FATAL; - } - - libpts_init(); - - if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1) - { - DBG1(DBG_IMC, "no common IF-IMC version"); - return TNC_RESULT_NO_COMMON_VERSION; - } - return TNC_RESULT_SUCCESS; -} - -/** - * see section 3.7.2 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id, - TNC_ConnectionID connection_id, - TNC_ConnectionState new_state) -{ - imc_state_t *state; - /* TODO: Not used so far */ - //imc_attestation_state_t *attestation_state; - - if (!imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); - return TNC_RESULT_NOT_INITIALIZED; - } - switch (new_state) - { - case TNC_CONNECTION_STATE_CREATE: - state = imc_attestation_state_create(connection_id); - return imc_attestation->create_state(imc_attestation, state); - case TNC_CONNECTION_STATE_DELETE: - return imc_attestation->delete_state(imc_attestation, connection_id); - case TNC_CONNECTION_STATE_HANDSHAKE: - case TNC_CONNECTION_STATE_ACCESS_ISOLATED: - case TNC_CONNECTION_STATE_ACCESS_NONE: - default: - return imc_attestation->change_state(imc_attestation, connection_id, - new_state, NULL); - } -} - - -/** - * see section 3.7.3 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id, - TNC_ConnectionID connection_id) -{ - imc_state_t *state; - imc_attestation_state_t *attestation_state; - pts_t *pts; - char *platform_info; - TNC_Result result = TNC_RESULT_SUCCESS; - - if (!imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); - return TNC_RESULT_NOT_INITIALIZED; - } - - /* get current IMC state */ - if (!imc_attestation->get_state(imc_attestation, connection_id, &state)) - { - return TNC_RESULT_FATAL; - } - attestation_state = (imc_attestation_state_t*)state; - pts = attestation_state->get_pts(attestation_state); - - platform_info = pts->get_platform_info(pts); - if (platform_info) - { - pa_tnc_msg_t *pa_tnc_msg; - pa_tnc_attr_t *attr; - - pa_tnc_msg = pa_tnc_msg_create(); - attr = ietf_attr_product_info_create(0, 0, platform_info); - pa_tnc_msg->add_attribute(pa_tnc_msg, attr); - pa_tnc_msg->build(pa_tnc_msg); - result = imc_attestation->send_message(imc_attestation, connection_id, - pa_tnc_msg->get_encoding(pa_tnc_msg)); - pa_tnc_msg->destroy(pa_tnc_msg); - } - - return result; -} - -/** - * see section 3.7.4 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, - TNC_ConnectionID connection_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type) -{ - pa_tnc_msg_t *pa_tnc_msg; - pa_tnc_attr_t *attr; - linked_list_t *attr_list; - imc_state_t *state; - imc_attestation_state_t *attestation_state; - enumerator_t *enumerator; - pts_t *pts; - TNC_Result result; - bool fatal_error = FALSE; - - if (!imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); - return TNC_RESULT_NOT_INITIALIZED; - } - - /* get current IMC state */ - if (!imc_attestation->get_state(imc_attestation, connection_id, &state)) - { - return TNC_RESULT_FATAL; - } - attestation_state = (imc_attestation_state_t*)state; - pts = attestation_state->get_pts(attestation_state); - - /* parse received PA-TNC message and automatically handle any errors */ - result = imc_attestation->receive_message(imc_attestation, connection_id, - chunk_create(msg, msg_len), msg_type, - &pa_tnc_msg); - - /* no parsed PA-TNC attributes available if an error occurred */ - if (!pa_tnc_msg) - { - return result; - } - - attr_list = linked_list_create(); - - /* analyze PA-TNC attributes */ - enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); - while (enumerator->enumerate(enumerator, &attr)) - { - if (attr->get_vendor_id(attr) == PEN_IETF && - attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) - { - ietf_attr_pa_tnc_error_t *error_attr; - pa_tnc_error_code_t error_code; - chunk_t msg_info, attr_info; - u_int32_t offset; - - error_attr = (ietf_attr_pa_tnc_error_t*)attr; - error_code = error_attr->get_error_code(error_attr); - msg_info = error_attr->get_msg_info(error_attr); - - DBG1(DBG_IMC, "received PA-TNC error '%N' concerning message %#B", - pa_tnc_error_code_names, error_code, &msg_info); - switch (error_code) - { - case PA_ERROR_INVALID_PARAMETER: - offset = error_attr->get_offset(error_attr); - DBG1(DBG_IMC, " occurred at offset of %u bytes", offset); - break; - case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_IMC, " unsupported attribute %#B", &attr_info); - break; - default: - break; - } - fatal_error = TRUE; - } - else if (attr->get_vendor_id(attr) == PEN_TCG) - { - switch (attr->get_type(attr)) - { - case TCG_PTS_REQ_PROTO_CAPS: - { - tcg_pts_attr_proto_caps_t *attr_cast; - pts_proto_caps_flag_t imc_flags, imv_flags; - - attr_cast = (tcg_pts_attr_proto_caps_t*)attr; - imv_flags = attr_cast->get_flags(attr_cast); - imc_flags = pts->get_proto_caps(pts); - pts->set_proto_caps(pts, imc_flags & imv_flags); - - /* Send PTS Protocol Capabilities attribute */ - attr = tcg_pts_attr_proto_caps_create(imc_flags & imv_flags, - FALSE); - attr_list->insert_last(attr_list, attr); - break; - } - case TCG_PTS_MEAS_ALGO: - { - tcg_pts_attr_meas_algo_t *attr_cast; - pts_meas_algorithms_t selected_algorithm; - - attr_cast = (tcg_pts_attr_meas_algo_t*)attr; - selected_algorithm = attr_cast->get_algorithms(attr_cast); - - if ((supported_algorithms & PTS_MEAS_ALGO_SHA384) && - (selected_algorithm & PTS_MEAS_ALGO_SHA384)) - { - pts->set_meas_algorithm(pts, PTS_MEAS_ALGO_SHA384); - } - else if ((supported_algorithms & PTS_MEAS_ALGO_SHA256) && - (selected_algorithm & PTS_MEAS_ALGO_SHA256)) - { - pts->set_meas_algorithm(pts, PTS_MEAS_ALGO_SHA256); - } - - else if ((supported_algorithms & PTS_MEAS_ALGO_SHA1) && - (selected_algorithm & PTS_MEAS_ALGO_SHA1)) - { - pts->set_meas_algorithm(pts, PTS_MEAS_ALGO_SHA1); - } - else - { - attr = pts_hash_alg_error_create(supported_algorithms); - attr_list->insert_last(attr_list, attr); - break; - } - - /* Send Measurement Algorithm Selection attribute */ - selected_algorithm = pts->get_meas_algorithm(pts); - attr = tcg_pts_attr_meas_algo_create(selected_algorithm, - TRUE); - attr_list->insert_last(attr_list, attr); - break; - } - - case TCG_PTS_GET_TPM_VERSION_INFO: - { - chunk_t tpm_version_info, attr_info; - - if (!pts->get_tpm_version_info(pts, &tpm_version_info)) - { - attr_info = attr->get_value(attr); - attr = ietf_attr_pa_tnc_error_create(PEN_TCG, - TCG_PTS_TPM_VERS_NOT_SUPPORTED, attr_info); - attr_list->insert_last(attr_list, attr); - break; - } - - /* Send TPM Version Info attribute */ - attr = tcg_pts_attr_tpm_version_info_create(tpm_version_info); - attr_list->insert_last(attr_list, attr); - break; - } - - case TCG_PTS_GET_AIK: - { - certificate_t *aik; - - aik = pts->get_aik(pts); - if (!aik) - { - DBG1(DBG_IMC, "no AIK certificate or public key available"); - break; - } - - /* Send AIK attribute */ - attr = tcg_pts_attr_aik_create(aik); - attr_list->insert_last(attr_list, attr); - break; - } - - /* PTS-based Attestation Evidence */ - case TCG_PTS_REQ_FUNCT_COMP_EVID: - break; - case TCG_PTS_GEN_ATTEST_EVID: - break; - case TCG_PTS_REQ_FILE_MEAS: - { - tcg_pts_attr_req_file_meas_t *attr_cast; - char *pathname; - u_int16_t request_id; - bool is_directory; - u_int32_t delimiter; - pts_file_meas_t *measurements; - pts_error_code_t pts_error; - chunk_t attr_info; - - attr_info = attr->get_value(attr); - attr_cast = (tcg_pts_attr_req_file_meas_t*)attr; - is_directory = attr_cast->get_directory_flag(attr_cast); - request_id = attr_cast->get_request_id(attr_cast); - delimiter = attr_cast->get_delimiter(attr_cast); - pathname = attr_cast->get_pathname(attr_cast); - - if (pts->is_path_valid(pts, pathname, &pts_error) && pts_error) - { - attr = ietf_attr_pa_tnc_error_create(PEN_TCG, - pts_error, attr_info); - attr_list->insert_last(attr_list, attr); - break; - } - else if (!pts->is_path_valid(pts, pathname, &pts_error)) - { - break; - } - - if (delimiter != SOLIDUS_UTF && delimiter != REVERSE_SOLIDUS_UTF) - { - attr = ietf_attr_pa_tnc_error_create(PEN_TCG, - TCG_PTS_INVALID_DELIMITER, attr_info); - attr_list->insert_last(attr_list, attr); - break; - } - - /* Do PTS File Measurements and send them to PTS-IMV */ - DBG2(DBG_IMC, "measurement request %d for %s '%s'", - request_id, is_directory ? "directory" : "file", - pathname); - measurements = pts->do_measurements(pts, request_id, - pathname, is_directory); - if (!measurements) - { - /* TODO handle error codes from measurements */ - return TNC_RESULT_FATAL; - } - attr = tcg_pts_attr_file_meas_create(measurements); - attr->set_noskip_flag(attr, TRUE); - attr_list->insert_last(attr_list, attr); - break; - } - /* TODO: Not implemented yet */ - case TCG_PTS_DH_NONCE_PARAMS_REQ: - case TCG_PTS_DH_NONCE_FINISH: - case TCG_PTS_REQ_FILE_META: - case TCG_PTS_REQ_INTEG_MEAS_LOG: - /* Attributes using XML */ - case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META: - case TCG_PTS_UPDATE_TEMPL_REF_MANI: - /* On Windows only*/ - case TCG_PTS_REQ_REGISTRY_VALUE: - /* Received on IMV side only*/ - case TCG_PTS_PROTO_CAPS: - case TCG_PTS_DH_NONCE_PARAMS_RESP: - case TCG_PTS_MEAS_ALGO_SELECTION: - case TCG_PTS_TPM_VERSION_INFO: - case TCG_PTS_TEMPL_REF_MANI_SET_META: - case TCG_PTS_AIK: - case TCG_PTS_SIMPLE_COMP_EVID: - case TCG_PTS_SIMPLE_EVID_FINAL: - case TCG_PTS_VERIFICATION_RESULT: - case TCG_PTS_INTEG_REPORT: - case TCG_PTS_UNIX_FILE_META: - case TCG_PTS_FILE_MEAS: - case TCG_PTS_INTEG_MEAS_LOG: - default: - DBG1(DBG_IMC, "received unsupported attribute '%N'", - tcg_attr_names, attr->get_type(attr)); - break; - } - } - } - enumerator->destroy(enumerator); - pa_tnc_msg->destroy(pa_tnc_msg); - - result = TNC_RESULT_SUCCESS; - - if (attr_list->get_count(attr_list)) - { - pa_tnc_msg = pa_tnc_msg_create(); - - enumerator = attr_list->create_enumerator(attr_list); - while (enumerator->enumerate(enumerator, &attr)) - { - pa_tnc_msg->add_attribute(pa_tnc_msg, attr); - } - enumerator->destroy(enumerator); - - pa_tnc_msg->build(pa_tnc_msg); - result = imc_attestation->send_message(imc_attestation, connection_id, - pa_tnc_msg->get_encoding(pa_tnc_msg)); - pa_tnc_msg->destroy(pa_tnc_msg); - } - attr_list->destroy(attr_list); - - return result; -} - -/** - * see section 3.7.5 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id, - TNC_ConnectionID connection_id) -{ - if (!imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); - return TNC_RESULT_NOT_INITIALIZED; - } - return TNC_RESULT_SUCCESS; -} - -/** - * see section 3.7.6 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id) -{ - if (!imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); - return TNC_RESULT_NOT_INITIALIZED; - } - - libpts_deinit(); - - imc_attestation->destroy(imc_attestation); - imc_attestation = NULL; - - return TNC_RESULT_SUCCESS; -} - -/** - * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id, - TNC_TNCC_BindFunctionPointer bind_function) -{ - if (!imc_attestation) - { - DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); - return TNC_RESULT_NOT_INITIALIZED; - } - return imc_attestation->bind_functions(imc_attestation, bind_function); -} diff --git a/src/libimcv/plugins/imc_scanner/imc_scanner.c b/src/libimcv/plugins/imc_scanner/imc_scanner.c index ecf758ba0..b24c39c3a 100644 --- a/src/libimcv/plugins/imc_scanner/imc_scanner.c +++ b/src/libimcv/plugins/imc_scanner/imc_scanner.c @@ -39,7 +39,7 @@ static const char imc_name[] = "Scanner"; static imc_agent_t *imc_scanner; /** - * see section 3.7.1 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id, TNC_Version min_version, @@ -66,7 +66,7 @@ TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id, } /** - * see section 3.7.2 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id, TNC_ConnectionID connection_id, @@ -237,15 +237,15 @@ static TNC_Result send_message(TNC_ConnectionID connection_id) msg = pa_tnc_msg_create(); msg->add_attribute(msg, attr); msg->build(msg); - result = imc_scanner->send_message(imc_scanner, connection_id, - msg->get_encoding(msg)); + result = imc_scanner->send_message(imc_scanner, connection_id, FALSE, 0, + TNC_IMVID_ANY, msg->get_encoding(msg)); msg->destroy(msg); return result; } /** - * see section 3.7.3 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id, TNC_ConnectionID connection_id) @@ -258,20 +258,19 @@ TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id, return send_message(connection_id); } -/** - * see section 3.7.4 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, +static TNC_Result receive_message(TNC_IMCID imc_id, TNC_ConnectionID connection_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_UInt32 msg_flags, + chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) { pa_tnc_msg_t *pa_tnc_msg; - pa_tnc_attr_t *attr; - enumerator_t *enumerator; + imc_state_t *state; TNC_Result result; - bool fatal_error = FALSE; + bool fatal_error; if (!imc_scanner) { @@ -279,10 +278,15 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, return TNC_RESULT_NOT_INITIALIZED; } + /* get current IMC state */ + if (!imc_scanner->get_state(imc_scanner, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + /* parse received PA-TNC message and automatically handle any errors */ - result = imc_scanner->receive_message(imc_scanner, connection_id, - chunk_create(msg, msg_len), msg_type, - &pa_tnc_msg); + result = imc_scanner->receive_message(imc_scanner, state, msg, msg_vid, + msg_subtype, src_imv_id, dst_imc_id, &pa_tnc_msg); /* no parsed PA-TNC attributes available if an error occurred */ if (!pa_tnc_msg) @@ -290,43 +294,8 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, return result; } - /* analyze PA-TNC attributes */ - enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); - while (enumerator->enumerate(enumerator, &attr)) - { - ietf_attr_pa_tnc_error_t *error_attr; - pa_tnc_error_code_t error_code; - chunk_t msg_info, attr_info; - u_int32_t offset; - - if (attr->get_vendor_id(attr) != PEN_IETF && - attr->get_type(attr) != IETF_ATTR_PA_TNC_ERROR) - { - continue; - } - - error_attr = (ietf_attr_pa_tnc_error_t*)attr; - error_code = error_attr->get_error_code(error_attr); - msg_info = error_attr->get_msg_info(error_attr); - DBG1(DBG_IMC, "received PA-TNC error '%N' concerning message %#B", - pa_tnc_error_code_names, error_code, &msg_info); - - switch (error_code) - { - case PA_ERROR_INVALID_PARAMETER: - offset = error_attr->get_offset(error_attr); - DBG1(DBG_IMC, " occurred at offset of %u bytes", offset); - break; - case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_IMC, " unsupported attribute %#B", &attr_info); - break; - default: - break; - } - fatal_error = TRUE; - } - enumerator->destroy(enumerator); + /* preprocess any IETF standard error attributes */ + fatal_error = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg); pa_tnc_msg->destroy(pa_tnc_msg); /* if no error occurred then always return the same response */ @@ -334,7 +303,44 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, } /** - * see section 3.7.5 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + + msg_vid = msg_type >> 8; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + + return receive_message(imc_id, connection_id, 0, chunk_create(msg, msg_len), + msg_vid, msg_subtype, 0, TNC_IMCID_ANY); +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMC_ReceiveMessageLong(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) +{ + return receive_message(imc_id, connection_id, msg_flags, + chunk_create(msg, msg_len), msg_vid, msg_subtype, + src_imv_id, dst_imc_id); +} + +/** + * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id, TNC_ConnectionID connection_id) @@ -348,7 +354,7 @@ TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id, } /** - * see section 3.7.6 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id) { @@ -364,7 +370,7 @@ TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id) } /** - * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.2 + * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id, TNC_TNCC_BindFunctionPointer bind_function) diff --git a/src/libimcv/plugins/imc_scanner/imc_scanner_state.c b/src/libimcv/plugins/imc_scanner/imc_scanner_state.c index dce7bca13..563105548 100644 --- a/src/libimcv/plugins/imc_scanner/imc_scanner_state.c +++ b/src/libimcv/plugins/imc_scanner/imc_scanner_state.c @@ -37,6 +37,17 @@ struct private_imc_scanner_state_t { * TNCCS connection state */ TNC_ConnectionState state; + + /** + * Does the TNCCS connection support long message types? + */ + bool has_long; + + /** + * Does the TNCCS connection support exclusive delivery? + */ + bool has_excl; + }; METHOD(imc_state_t, get_connection_id, TNC_ConnectionID, @@ -45,6 +56,25 @@ METHOD(imc_state_t, get_connection_id, TNC_ConnectionID, return this->connection_id; } +METHOD(imc_state_t, has_long, bool, + private_imc_scanner_state_t *this) +{ + return this->has_long; +} + +METHOD(imc_state_t, has_excl, bool, + private_imc_scanner_state_t *this) +{ + return this->has_excl; +} + +METHOD(imc_state_t, set_flags, void, + private_imc_scanner_state_t *this, bool has_long, bool has_excl) +{ + this->has_long = has_long; + this->has_excl = has_excl; +} + METHOD(imc_state_t, change_state, void, private_imc_scanner_state_t *this, TNC_ConnectionState new_state) { @@ -68,6 +98,9 @@ imc_state_t *imc_scanner_state_create(TNC_ConnectionID connection_id) .public = { .interface = { .get_connection_id = _get_connection_id, + .has_long = _has_long, + .has_excl = _has_excl, + .set_flags = _set_flags, .change_state = _change_state, .destroy = _destroy, }, diff --git a/src/libimcv/plugins/imc_test/imc_test.c b/src/libimcv/plugins/imc_test/imc_test.c index 01e70e8af..fe005ed4a 100644 --- a/src/libimcv/plugins/imc_test/imc_test.c +++ b/src/libimcv/plugins/imc_test/imc_test.c @@ -37,7 +37,7 @@ static const char imc_name[] = "Test"; static imc_agent_t *imc_test; /** - * see section 3.7.1 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id, TNC_Version min_version, @@ -64,7 +64,7 @@ TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id, } /** - * see section 3.7.2 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id, TNC_ConnectionID connection_id, @@ -75,21 +75,36 @@ TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id, TNC_Result result; char *command; bool retry; + int additional_ids; if (!imc_test) { DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); return TNC_RESULT_NOT_INITIALIZED; } + switch (new_state) { case TNC_CONNECTION_STATE_CREATE: command = lib->settings->get_str(lib->settings, - "libimcv.plugins.imc-test.command", "none"); + "libimcv.plugins.imc-test.command", "none"); retry = lib->settings->get_bool(lib->settings, "libimcv.plugins.imc-test.retry", FALSE); state = imc_test_state_create(connection_id, command, retry); - return imc_test->create_state(imc_test, state); + + result = imc_test->create_state(imc_test, state); + if (result != TNC_RESULT_SUCCESS) + { + return result; + } + + /* Optionally reserve additional IMC IDs */ + additional_ids = lib->settings->get_int(lib->settings, + "libimcv.plugins.imc-test.additional_ids", 0); + imc_test->reserve_additional_ids(imc_test, additional_ids - + imc_test->count_additional_ids(imc_test)); + + return TNC_RESULT_SUCCESS; case TNC_CONNECTION_STATE_HANDSHAKE: /* get updated IMC state */ @@ -139,56 +154,98 @@ TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id, } } -static TNC_Result send_message(TNC_ConnectionID connection_id) +static TNC_Result send_message(imc_state_t *state, TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) { + imc_test_state_t *test_state; pa_tnc_msg_t *msg; pa_tnc_attr_t *attr; - imc_state_t *state; - imc_test_state_t *test_state; + bool excl; + TNC_ConnectionID connection_id; TNC_Result result; - if (!imc_test->get_state(imc_test, connection_id, &state)) - { - return TNC_RESULT_FATAL; - } + connection_id = state->get_connection_id(state); test_state = (imc_test_state_t*)state; attr = ita_attr_command_create(test_state->get_command(test_state)); attr->set_noskip_flag(attr, TRUE); msg = pa_tnc_msg_create(); msg->add_attribute(msg, attr); msg->build(msg); - result = imc_test->send_message(imc_test, connection_id, - msg->get_encoding(msg)); + excl = dst_imv_id != TNC_IMVID_ANY; + result = imc_test->send_message(imc_test, connection_id, excl, src_imc_id, + dst_imv_id, msg->get_encoding(msg)); msg->destroy(msg); return result; } /** - * see section 3.7.3 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id, TNC_ConnectionID connection_id) { + imc_state_t *state; + enumerator_t *enumerator; + void *pointer; + TNC_UInt32 additional_id; + TNC_Result result; + if (!imc_test) { DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); return TNC_RESULT_NOT_INITIALIZED; } - return send_message(connection_id); + + /* get current IMC state */ + if (!imc_test->get_state(imc_test, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + + /* send PA message for primary IMC ID */ + result = send_message(state, imc_id, TNC_IMVID_ANY); + + /* Exit if there are no additional IMC IDs */ + if (!imc_test->count_additional_ids(imc_test)) + { + return result; + } + + /* Do we have support for transporting multiple IMC IDs? */ + if (!state->has_long(state)) + { + DBG1(DBG_IMC, "IMC %u \"%s\" did not detect support for transporting " + "multiple IMC IDs", imc_id, imc_name); + return result; + } + + /* send PA messages for additional IMC IDs */ + enumerator = imc_test->create_id_enumerator(imc_test); + while (result == TNC_RESULT_SUCCESS && + enumerator->enumerate(enumerator, &pointer)) + { + /* interpret pointer as scalar value */ + additional_id = (TNC_UInt32)pointer; + result = send_message(state, additional_id, TNC_IMVID_ANY); + } + enumerator->destroy(enumerator); + + return result; } -/** - * see section 3.7.4 of TCG TNC IF-IMC Specification 1.2 - */ -TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, +static TNC_Result receive_message(TNC_IMCID imc_id, TNC_ConnectionID connection_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_UInt32 msg_flags, + chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) { pa_tnc_msg_t *pa_tnc_msg; pa_tnc_attr_t *attr; + imc_state_t *state; enumerator_t *enumerator; TNC_Result result; bool fatal_error = FALSE; @@ -199,10 +256,15 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, return TNC_RESULT_NOT_INITIALIZED; } + /* get current IMC state */ + if (!imc_test->get_state(imc_test, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + /* parse received PA-TNC message and automatically handle any errors */ - result = imc_test->receive_message(imc_test, connection_id, - chunk_create(msg, msg_len), msg_type, - &pa_tnc_msg); + result = imc_test->receive_message(imc_test, state, msg, msg_vid, + msg_subtype, src_imv_id, dst_imc_id, &pa_tnc_msg); /* no parsed PA-TNC attributes available if an error occurred */ if (!pa_tnc_msg) @@ -210,41 +272,15 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, return result; } + /* preprocess any IETF standard error attributes */ + fatal_error = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg); + /* analyze PA-TNC attributes */ enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); while (enumerator->enumerate(enumerator, &attr)) { - if (attr->get_vendor_id(attr) == PEN_IETF && - attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) - { - ietf_attr_pa_tnc_error_t *error_attr; - pa_tnc_error_code_t error_code; - chunk_t msg_info, attr_info; - u_int32_t offset; - - error_attr = (ietf_attr_pa_tnc_error_t*)attr; - error_code = error_attr->get_error_code(error_attr); - msg_info = error_attr->get_msg_info(error_attr); - - DBG1(DBG_IMC, "received PA-TNC error '%N' concerning message %#B", - pa_tnc_error_code_names, error_code, &msg_info); - switch (error_code) - { - case PA_ERROR_INVALID_PARAMETER: - offset = error_attr->get_offset(error_attr); - DBG1(DBG_IMC, " occurred at offset of %u bytes", offset); - break; - case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_IMC, " unsupported attribute %#B", &attr_info); - break; - default: - break; - } - fatal_error = TRUE; - } - else if (attr->get_vendor_id(attr) == PEN_ITA && - attr->get_type(attr) == ITA_ATTR_COMMAND) + if (attr->get_vendor_id(attr) == PEN_ITA && + attr->get_type(attr) == ITA_ATTR_COMMAND) { ita_attr_command_t *ita_attr; char *command; @@ -257,11 +293,49 @@ TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, pa_tnc_msg->destroy(pa_tnc_msg); /* if no error occurred then always return the same response */ - return fatal_error ? TNC_RESULT_FATAL : send_message(connection_id); + return fatal_error ? TNC_RESULT_FATAL : + send_message(state, dst_imc_id, src_imv_id); +} + +/** + * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + + msg_vid = msg_type >> 8; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + + return receive_message(imc_id, connection_id, 0, chunk_create(msg, msg_len), + msg_vid, msg_subtype, 0, TNC_IMCID_ANY); +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMC_ReceiveMessageLong(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) +{ + return receive_message(imc_id, connection_id, msg_flags, + chunk_create(msg, msg_len), msg_vid, msg_subtype, + src_imv_id, dst_imc_id); } /** - * see section 3.7.5 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id, TNC_ConnectionID connection_id) @@ -275,7 +349,7 @@ TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id, } /** - * see section 3.7.6 of TCG TNC IF-IMC Specification 1.2 + * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id) { @@ -291,7 +365,7 @@ TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id) } /** - * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.2 + * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3 */ TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id, TNC_TNCC_BindFunctionPointer bind_function) diff --git a/src/libimcv/plugins/imc_test/imc_test_state.c b/src/libimcv/plugins/imc_test/imc_test_state.c index cc7e18a4d..2adfd7d64 100644 --- a/src/libimcv/plugins/imc_test/imc_test_state.c +++ b/src/libimcv/plugins/imc_test/imc_test_state.c @@ -15,6 +15,7 @@ #include "imc_test_state.h" #include <debug.h> +#include <utils/linked_list.h> typedef struct private_imc_test_state_t private_imc_test_state_t; @@ -39,6 +40,16 @@ struct private_imc_test_state_t { TNC_ConnectionState state; /** + * Does the TNCCS connection support long message types? + */ + bool has_long; + + /** + * Does the TNCCS connection support exclusive delivery? + */ + bool has_excl; + + /** * Command to transmit to IMV */ char *command; @@ -52,6 +63,7 @@ struct private_imc_test_state_t { * Do a handshake retry */ bool handshake_retry; + }; METHOD(imc_state_t, get_connection_id, TNC_ConnectionID, @@ -60,6 +72,25 @@ METHOD(imc_state_t, get_connection_id, TNC_ConnectionID, return this->connection_id; } +METHOD(imc_state_t, has_long, bool, + private_imc_test_state_t *this) +{ + return this->has_long; +} + +METHOD(imc_state_t, has_excl, bool, + private_imc_test_state_t *this) +{ + return this->has_excl; +} + +METHOD(imc_state_t, set_flags, void, + private_imc_test_state_t *this, bool has_long, bool has_excl) +{ + this->has_long = has_long; + this->has_excl = has_excl; +} + METHOD(imc_state_t, change_state, void, private_imc_test_state_t *this, TNC_ConnectionState new_state) { @@ -123,6 +154,9 @@ imc_state_t *imc_test_state_create(TNC_ConnectionID connection_id, .public = { .interface = { .get_connection_id = _get_connection_id, + .has_long = _has_long, + .has_excl = _has_excl, + .set_flags = _set_flags, .change_state = _change_state, .destroy = _destroy, }, diff --git a/src/libimcv/plugins/imc_test/imc_test_state.h b/src/libimcv/plugins/imc_test/imc_test_state.h index 384285af8..d9160df94 100644 --- a/src/libimcv/plugins/imc_test/imc_test_state.h +++ b/src/libimcv/plugins/imc_test/imc_test_state.h @@ -21,6 +21,7 @@ #ifndef IMC_TEST_STATE_H_ #define IMC_TEST_STATE_H_ +#include <tncifimc.h> #include <imc/imc_state.h> #include <library.h> @@ -63,6 +64,7 @@ struct imc_test_state_t { * @return TRUE if a handshake retry should be done */ bool (*do_handshake_retry)(imc_test_state_t *this); + }; /** diff --git a/src/libimcv/plugins/imv_attestation/Makefile.am b/src/libimcv/plugins/imv_attestation/Makefile.am deleted file mode 100644 index bfff6e877..000000000 --- a/src/libimcv/plugins/imv_attestation/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ - -INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libtncif \ - -I$(top_srcdir)/src/libimcv -I$(top_srcdir)/src/libpts - -AM_CFLAGS = -rdynamic - -imcv_LTLIBRARIES = imv-attestation.la - -imv_attestation_la_LIBADD = $(top_builddir)/src/libimcv/libimcv.la \ - $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(top_builddir)/src/libpts/libpts.la - -imv_attestation_la_SOURCES = imv_attestation.c \ - imv_attestation_state.h imv_attestation_state.c - -imv_attestation_la_LDFLAGS = -module -avoid-version - diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation.c b/src/libimcv/plugins/imv_attestation/imv_attestation.c deleted file mode 100644 index 86de5a9cf..000000000 --- a/src/libimcv/plugins/imv_attestation/imv_attestation.c +++ /dev/null @@ -1,695 +0,0 @@ -/* - * Copyright (C) 2011 Sansar Choinyambuu - * 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 "imv_attestation_state.h" - -#include <imv/imv_agent.h> -#include <pa_tnc/pa_tnc_msg.h> -#include <ietf/ietf_attr.h> -#include <ietf/ietf_attr_pa_tnc_error.h> -#include <ietf/ietf_attr_product_info.h> - -#include <libpts.h> - -#include <pts/pts_database.h> -#include <pts/pts_creds.h> -#include <pts/pts_error.h> - -#include <tcg/tcg_attr.h> -#include <tcg/tcg_pts_attr_proto_caps.h> -#include <tcg/tcg_pts_attr_meas_algo.h> -#include <tcg/tcg_pts_attr_get_tpm_version_info.h> -#include <tcg/tcg_pts_attr_tpm_version_info.h> -#include <tcg/tcg_pts_attr_get_aik.h> -#include <tcg/tcg_pts_attr_aik.h> -#include <tcg/tcg_pts_attr_req_funct_comp_evid.h> -#include <tcg/tcg_pts_attr_gen_attest_evid.h> -#include <tcg/tcg_pts_attr_simple_comp_evid.h> -#include <tcg/tcg_pts_attr_simple_evid_final.h> -#include <tcg/tcg_pts_attr_req_file_meas.h> -#include <tcg/tcg_pts_attr_file_meas.h> - -#include <tncif_pa_subtypes.h> - -#include <pen/pen.h> -#include <debug.h> -#include <credentials/credential_manager.h> - -/* IMV definitions */ - -static const char imv_name[] = "Attestation"; - -#define IMV_VENDOR_ID PEN_TCG -#define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS - -static imv_agent_t *imv_attestation; - -/** - * Supported PTS measurement algorithms - */ -static pts_meas_algorithms_t supported_algorithms = 0; - -/** - * PTS file measurement database - */ -static pts_database_t *pts_db; - -/** - * PTS credentials - */ -static pts_creds_t *pts_creds; - -/** - * PTS credential manager - */ -static credential_manager_t *pts_credmgr; - -/** - * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, - TNC_Version min_version, - TNC_Version max_version, - TNC_Version *actual_version) -{ - char *hash_alg, *uri, *cadir; - - if (imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name); - return TNC_RESULT_ALREADY_INITIALIZED; - } - if (!pts_meas_probe_algorithms(&supported_algorithms)) - { - return TNC_RESULT_FATAL; - } - imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE, - imv_id, actual_version); - if (!imv_attestation) - { - return TNC_RESULT_FATAL; - } - - libpts_init(); - - if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1) - { - DBG1(DBG_IMV, "no common IF-IMV version"); - return TNC_RESULT_NO_COMMON_VERSION; - } - - /** - * Specify supported PTS measurement algorithms - * - * sha1 : PTS_MEAS_ALGO_SHA1 - * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 - * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384 - * - * we expect the PTS-IMC to select the strongest supported algorithm - */ - hash_alg = lib->settings->get_str(lib->settings, - "libimcv.plugins.imv-attestation.hash_algorithm", "sha256"); - if (!strcaseeq(hash_alg, "sha384") && !strcaseeq(hash_alg, "sha2_384")) - { - /* remove SHA384 algorithm */ - supported_algorithms &= ~PTS_MEAS_ALGO_SHA384; - } - if (strcaseeq(hash_alg, "sha1")) - { - /* remove SHA256 algorithm */ - supported_algorithms &= ~PTS_MEAS_ALGO_SHA256; - } - - /* create a PTS credential manager */ - pts_credmgr = credential_manager_create(); - - /* create PTS credential set */ - cadir = lib->settings->get_str(lib->settings, - "libimcv.plugins.imv-attestation.cadir", NULL); - pts_creds = pts_creds_create(cadir); - if (pts_creds) - { - pts_credmgr->add_set(pts_credmgr, pts_creds->get_set(pts_creds)); - } - - /* attach file measurement database */ - uri = lib->settings->get_str(lib->settings, - "libimcv.plugins.imv-attestation.database", NULL); - pts_db = pts_database_create(uri); - - return TNC_RESULT_SUCCESS; -} - -/** - * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, - TNC_ConnectionID connection_id, - TNC_ConnectionState new_state) -{ - imv_state_t *state; - imv_attestation_state_t *attestation_state; - TNC_Result result; - - if (!imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); - return TNC_RESULT_NOT_INITIALIZED; - } - switch (new_state) - { - case TNC_CONNECTION_STATE_CREATE: - state = imv_attestation_state_create(connection_id); - return imv_attestation->create_state(imv_attestation, state); - case TNC_CONNECTION_STATE_DELETE: - return imv_attestation->delete_state(imv_attestation, connection_id); - case TNC_CONNECTION_STATE_HANDSHAKE: - result = imv_attestation->change_state(imv_attestation, connection_id, - new_state, &state); - if (result != TNC_RESULT_SUCCESS) - { - return result; - } - attestation_state = (imv_attestation_state_t*)state; - - /* TODO: Get some configurations */ - - return TNC_RESULT_SUCCESS; - default: - return imv_attestation->change_state(imv_attestation, connection_id, - new_state, NULL); - } -} - -static TNC_Result send_message(TNC_ConnectionID connection_id) -{ - pa_tnc_msg_t *msg; - pa_tnc_attr_t *attr; - pts_t *pts; - imv_state_t *state; - imv_attestation_state_t *attestation_state; - imv_attestation_handshake_state_t handshake_state; - TNC_Result result; - - if (!imv_attestation->get_state(imv_attestation, connection_id, &state)) - { - return TNC_RESULT_FATAL; - } - attestation_state = (imv_attestation_state_t*)state; - handshake_state = attestation_state->get_handshake_state(attestation_state); - pts = attestation_state->get_pts(attestation_state); - - msg = pa_tnc_msg_create(); - - - /* Switch on the attribute type IMV has received */ - switch (handshake_state) - { - case IMV_ATTESTATION_STATE_INIT: - { - pts_proto_caps_flag_t flags; - - /* Send Request Protocol Capabilities attribute */ - flags = pts->get_proto_caps(pts); - attr = tcg_pts_attr_proto_caps_create(flags, TRUE); - attr->set_noskip_flag(attr, TRUE); - msg->add_attribute(msg, attr); - - /* Send Measurement Algorithms attribute */ - attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE); - attr->set_noskip_flag(attr, TRUE); - msg->add_attribute(msg, attr); - - attestation_state->set_handshake_state(attestation_state, - IMV_ATTESTATION_STATE_MEAS); - break; - } - - case IMV_ATTESTATION_STATE_MEAS: - { - enumerator_t *enumerator; - u_int32_t delimiter = SOLIDUS_UTF; - char *platform_info, *pathname; - u_int16_t request_id; - int id, type; - bool is_dir; - - attestation_state->set_handshake_state(attestation_state, - IMV_ATTESTATION_STATE_END); - - /* Does the PTS-IMC have TPM support? */ - if (pts->get_proto_caps(pts) & PTS_PROTO_CAPS_T) - { - /* Send Get TPM Version attribute */ - attr = tcg_pts_attr_get_tpm_version_info_create(); - attr->set_noskip_flag(attr, TRUE); - msg->add_attribute(msg, attr); - - /* Send Get AIK attribute */ - attr = tcg_pts_attr_get_aik_create(); - attr->set_noskip_flag(attr, TRUE); - msg->add_attribute(msg, attr); - } - - /* Get Platform and OS of the PTS-IMC */ - platform_info = pts->get_platform_info(pts); - - if (!pts_db || !platform_info) - { - DBG1(DBG_IMV, "%s%s%s not available", - (pts_db) ? "" : "pts database", - (!pts_db && !platform_info) ? "and" : "", - (platform_info) ? "" : "platform info"); - break; - } - DBG1(DBG_IMV, "platform is '%s'", platform_info); - - /* Send Request File Measurement attribute */ - enumerator = pts_db->create_file_enumerator(pts_db, platform_info); - if (!enumerator) - { - break; - } - while (enumerator->enumerate(enumerator, &id, &type, &pathname)) - { - is_dir = (type != 0); - request_id = attestation_state->add_request(attestation_state, - id, is_dir); - DBG2(DBG_IMV, "measurement request %d for %s '%s'", - request_id, is_dir ? "directory" : "file", pathname); - attr = tcg_pts_attr_req_file_meas_create(is_dir, request_id, - delimiter, pathname); - attr->set_noskip_flag(attr, TRUE); - msg->add_attribute(msg, attr); - } - enumerator->destroy(enumerator); - break; - } - case IMV_ATTESTATION_STATE_COMP_EVID: - case IMV_ATTESTATION_STATE_IML: - DBG1(DBG_IMV, "Attestation IMV has nothing to send: \"%s\"", - handshake_state); - return TNC_RESULT_FATAL; - default: - DBG1(DBG_IMV, "Attestation IMV is in unknown state: \"%s\"", - handshake_state); - return TNC_RESULT_FATAL; - } - - msg->build(msg); - result = imv_attestation->send_message(imv_attestation, connection_id, - msg->get_encoding(msg)); - msg->destroy(msg); - - return result; -} - -/** - * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, - TNC_ConnectionID connection_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type) -{ - pa_tnc_msg_t *pa_tnc_msg; - pa_tnc_attr_t *attr; - imv_state_t *state; - imv_attestation_state_t *attestation_state; - pts_t *pts; - enumerator_t *enumerator; - TNC_Result result; - bool fatal_error = FALSE; - bool measurement_error = FALSE; - - if (!imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); - return TNC_RESULT_NOT_INITIALIZED; - } - - /* get current IMV state */ - if (!imv_attestation->get_state(imv_attestation, connection_id, &state)) - { - return TNC_RESULT_FATAL; - } - attestation_state = (imv_attestation_state_t*)state; - pts = attestation_state->get_pts(attestation_state); - - /* parse received PA-TNC message and automatically handle any errors */ - result = imv_attestation->receive_message(imv_attestation, connection_id, - chunk_create(msg, msg_len), msg_type, - &pa_tnc_msg); - - /* no parsed PA-TNC attributes available if an error occurred */ - if (!pa_tnc_msg) - { - return result; - } - - /* analyze PA-TNC attributes */ - enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); - while (enumerator->enumerate(enumerator, &attr)) - { - if (attr->get_vendor_id(attr) == PEN_IETF) - { - if (attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) - { - ietf_attr_pa_tnc_error_t *error_attr; - pen_t error_vendor_id; - pa_tnc_error_code_t error_code; - chunk_t msg_info, attr_info; - u_int32_t offset; - - error_attr = (ietf_attr_pa_tnc_error_t*)attr; - error_vendor_id = error_attr->get_vendor_id(error_attr); - error_code = error_attr->get_error_code(error_attr); - msg_info = error_attr->get_msg_info(error_attr); - - if (error_vendor_id == PEN_IETF) - { - DBG1(DBG_IMV, "received PA-TNC error '%N' " - "concerning message %#B", - pa_tnc_error_code_names, error_code, &msg_info); - - switch (error_code) - { - case PA_ERROR_INVALID_PARAMETER: - offset = error_attr->get_offset(error_attr); - DBG1(DBG_IMV, " occurred at offset of %u bytes", - offset); - break; - case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_IMV, " unsupported attribute %#B", - &attr_info); - break; - default: - break; - } - } - else if (error_vendor_id == PEN_TCG) - { - DBG1(DBG_IMV, "received TCG-PTS error '%N'", - pts_error_code_names, error_code); - DBG1(DBG_IMV, "error information: %B", &msg_info); - } - fatal_error = TRUE; - } - else if (attr->get_type(attr) == IETF_ATTR_PRODUCT_INFORMATION) - { - ietf_attr_product_info_t *attr_cast; - char *platform_info; - - attr_cast = (ietf_attr_product_info_t*)attr; - platform_info = attr_cast->get_info(attr_cast, NULL, NULL); - pts->set_platform_info(pts, platform_info); - } - } - else if (attr->get_vendor_id(attr) == PEN_TCG) - { - switch (attr->get_type(attr)) - { - case TCG_PTS_PROTO_CAPS: - { - tcg_pts_attr_proto_caps_t *attr_cast; - pts_proto_caps_flag_t flags; - - attr_cast = (tcg_pts_attr_proto_caps_t*)attr; - flags = attr_cast->get_flags(attr_cast); - pts->set_proto_caps(pts, flags); - break; - } - case TCG_PTS_MEAS_ALGO_SELECTION: - { - tcg_pts_attr_meas_algo_t *attr_cast; - pts_meas_algorithms_t selected_algorithm; - - attr_cast = (tcg_pts_attr_meas_algo_t*)attr; - selected_algorithm = attr_cast->get_algorithms(attr_cast); - pts->set_meas_algorithm(pts, selected_algorithm); - break; - } - case TCG_PTS_TPM_VERSION_INFO: - { - tcg_pts_attr_tpm_version_info_t *attr_cast; - chunk_t tpm_version_info; - - attr_cast = (tcg_pts_attr_tpm_version_info_t*)attr; - tpm_version_info = attr_cast->get_tpm_version_info(attr_cast); - pts->set_tpm_version_info(pts, tpm_version_info); - break; - } - case TCG_PTS_AIK: - { - tcg_pts_attr_aik_t *attr_cast; - certificate_t *aik, *issuer; - enumerator_t *e; - bool trusted = FALSE; - - attr_cast = (tcg_pts_attr_aik_t*)attr; - aik = attr_cast->get_aik(attr_cast); - if (!aik) - { - /* TODO generate error attribute */ - break; - } - if (aik->get_type(aik) == CERT_X509) - { - DBG1(DBG_IMV, "verifying AIK certificate"); - e = pts_credmgr->create_trusted_enumerator(pts_credmgr, - KEY_ANY, aik->get_issuer(aik), FALSE); - while (e->enumerate(e, &issuer)) - { - if (aik->issued_by(aik, issuer)) - { - trusted = TRUE; - break; - } - } - e->destroy(e); - DBG1(DBG_IMV, "AIK certificate is %strusted", - trusted ? "" : "not "); - } - pts->set_aik(pts, aik); - break; - } - - /* PTS-based Attestation Evidence */ - case TCG_PTS_SIMPLE_COMP_EVID: - break; - case TCG_PTS_SIMPLE_EVID_FINAL: - break; - case TCG_PTS_FILE_MEAS: - { - tcg_pts_attr_file_meas_t *attr_cast; - u_int16_t request_id; - int file_count, file_id; - pts_meas_algorithms_t algo; - pts_file_meas_t *measurements; - char *platform_info; - enumerator_t *e_hash; - bool is_dir; - - platform_info = pts->get_platform_info(pts); - if (!pts_db || !platform_info) - { - break; - } - - attr_cast = (tcg_pts_attr_file_meas_t*)attr; - measurements = attr_cast->get_measurements(attr_cast); - algo = pts->get_meas_algorithm(pts); - request_id = measurements->get_request_id(measurements); - file_count = measurements->get_file_count(measurements); - - DBG1(DBG_IMV, "measurement request %d returned %d file%s:", - request_id, file_count, (file_count == 1) ? "":"s"); - - if (!attestation_state->check_off_request(attestation_state, - request_id, &file_id, &is_dir)) - { - DBG1(DBG_IMV, " no entry found for this request"); - break; - } - - /* check hashes from database against measurements */ - e_hash = pts_db->create_hash_enumerator(pts_db, - platform_info, algo, file_id, is_dir); - if (!measurements->verify(measurements, e_hash, is_dir)) - { - measurement_error = TRUE; - } - e_hash->destroy(e_hash); - break; - } - - /* TODO: Not implemented yet */ - case TCG_PTS_DH_NONCE_PARAMS_RESP: - case TCG_PTS_UNIX_FILE_META: - case TCG_PTS_INTEG_MEAS_LOG: - /* Attributes using XML */ - case TCG_PTS_TEMPL_REF_MANI_SET_META: - case TCG_PTS_VERIFICATION_RESULT: - case TCG_PTS_INTEG_REPORT: - /* On Windows only*/ - case TCG_PTS_WIN_FILE_META: - case TCG_PTS_REGISTRY_VALUE: - /* Received on IMC side only*/ - case TCG_PTS_REQ_PROTO_CAPS: - case TCG_PTS_DH_NONCE_PARAMS_REQ: - case TCG_PTS_DH_NONCE_FINISH: - case TCG_PTS_MEAS_ALGO: - case TCG_PTS_GET_TPM_VERSION_INFO: - case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META: - case TCG_PTS_UPDATE_TEMPL_REF_MANI: - case TCG_PTS_GET_AIK: - case TCG_PTS_REQ_FUNCT_COMP_EVID: - case TCG_PTS_GEN_ATTEST_EVID: - case TCG_PTS_REQ_FILE_META: - case TCG_PTS_REQ_FILE_MEAS: - case TCG_PTS_REQ_INTEG_MEAS_LOG: - default: - DBG1(DBG_IMV, "received unsupported attribute '%N'", - tcg_attr_names, attr->get_type(attr)); - break; - } - } - } - enumerator->destroy(enumerator); - pa_tnc_msg->destroy(pa_tnc_msg); - - - if (fatal_error) - { - state->set_recommendation(state, - TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, - TNC_IMV_EVALUATION_RESULT_ERROR); - return imv_attestation->provide_recommendation(imv_attestation, - connection_id); - } - - if (attestation_state->get_handshake_state(attestation_state) & - IMV_ATTESTATION_STATE_END) - { - if (attestation_state->get_request_count(attestation_state)) - { - DBG1(DBG_IMV, "failure due to %d pending file measurements", - attestation_state->get_request_count(attestation_state)); - measurement_error = TRUE; - } - if (measurement_error) - { - state->set_recommendation(state, - TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, - TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR); - } - else - { - state->set_recommendation(state, - TNC_IMV_ACTION_RECOMMENDATION_ALLOW, - TNC_IMV_EVALUATION_RESULT_COMPLIANT); - } - return imv_attestation->provide_recommendation(imv_attestation, - connection_id); - } - - return send_message(connection_id); -} - -/** - * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, - TNC_ConnectionID connection_id) -{ - if (!imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); - return TNC_RESULT_NOT_INITIALIZED; - } - return imv_attestation->provide_recommendation(imv_attestation, connection_id); -} - -/** - * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, - TNC_ConnectionID connection_id) -{ - imv_state_t *state; - imv_attestation_state_t *attestation_state; - - if (!imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); - return TNC_RESULT_NOT_INITIALIZED; - } - /* get current IMV state */ - if (!imv_attestation->get_state(imv_attestation, connection_id, &state)) - { - return TNC_RESULT_FATAL; - } - attestation_state = (imv_attestation_state_t*)state; - - /* Check if IMV has to initiate the PA-TNC exchange */ - if (attestation_state->get_handshake_state(attestation_state) == - IMV_ATTESTATION_STATE_INIT) - { - return send_message(connection_id); - } - return TNC_RESULT_SUCCESS; -} - -/** - * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) -{ - if (!imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); - return TNC_RESULT_NOT_INITIALIZED; - } - if (pts_creds) - { - pts_credmgr->remove_set(pts_credmgr, pts_creds->get_set(pts_creds)); - pts_creds->destroy(pts_creds); - } - DESTROY_IF(pts_db); - DESTROY_IF(pts_credmgr); - - libpts_deinit(); - - imv_attestation->destroy(imv_attestation); - imv_attestation = NULL; - - return TNC_RESULT_SUCCESS; -} - -/** - * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id, - TNC_TNCS_BindFunctionPointer bind_function) -{ - if (!imv_attestation) - { - DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); - return TNC_RESULT_NOT_INITIALIZED; - } - return imv_attestation->bind_functions(imv_attestation, bind_function); -} diff --git a/src/libimcv/plugins/imv_attestation/tables.sql b/src/libimcv/plugins/imv_attestation/tables.sql deleted file mode 100644 index 8cc0e5588..000000000 --- a/src/libimcv/plugins/imv_attestation/tables.sql +++ /dev/null @@ -1,36 +0,0 @@ -/* PTS SQLite database */ - -DROP TABLE IF EXISTS files; -CREATE TABLE files ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - type INTEGER NOT NULL, - path TEXT NOT NULL -); - -DROP TABLE IF EXISTS products; -CREATE TABLE products ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL -); -DROP INDEX IF EXISTS products_name; -CREATE INDEX products_name ON products ( - name -); - -DROP TABLE IF EXISTS product_file; -CREATE TABLE product_file ( - product INTEGER NOT NULL, - file INTEGER NOT NULL, - PRIMARY KEY (product, file) -); - -DROP TABLE IF EXISTS file_hashes; -CREATE TABLE file_hashes ( - file INTEGER NOT NULL, - directory INTEGER DEFAULT 0, - product INTEGER NOT NULL, - algo INTEGER NOT NULL, - hash BLOB NOT NULL, - PRIMARY KEY(file, directory, product, algo) -); - diff --git a/src/libimcv/plugins/imv_scanner/imv_scanner.c b/src/libimcv/plugins/imv_scanner/imv_scanner.c index 5561e6737..845511555 100644 --- a/src/libimcv/plugins/imv_scanner/imv_scanner.c +++ b/src/libimcv/plugins/imv_scanner/imv_scanner.c @@ -111,7 +111,7 @@ static linked_list_t* get_port_list(char *label) /* - * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, TNC_Version min_version, @@ -149,7 +149,7 @@ TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, } /** - * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, TNC_ConnectionID connection_id, @@ -175,21 +175,21 @@ TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, } } -/** - * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, +static TNC_Result receive_message(TNC_IMVID imv_id, TNC_ConnectionID connection_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_UInt32 msg_flags, + chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) { pa_tnc_msg_t *pa_tnc_msg; pa_tnc_attr_t *attr; imv_state_t *state; enumerator_t *enumerator; TNC_Result result; - bool fatal_error = FALSE; + bool fatal_error; if (!imv_scanner) { @@ -204,9 +204,8 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, } /* parse received PA-TNC message and automatically handle any errors */ - result = imv_scanner->receive_message(imv_scanner, connection_id, - chunk_create(msg, msg_len), msg_type, - &pa_tnc_msg); + result = imv_scanner->receive_message(imv_scanner, state, msg, msg_vid, + msg_subtype, src_imc_id, dst_imv_id, &pa_tnc_msg); /* no parsed PA-TNC attributes available if an error occurred */ if (!pa_tnc_msg) @@ -214,44 +213,15 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, return result; } + /* preprocess any IETF standard error attributes */ + fatal_error = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg); + /* analyze PA-TNC attributes */ enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); while (enumerator->enumerate(enumerator, &attr)) { - if (attr->get_vendor_id(attr) != PEN_IETF) - { - continue; - } - - if (attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) - { - ietf_attr_pa_tnc_error_t *error_attr; - pa_tnc_error_code_t error_code; - chunk_t msg_info, attr_info; - u_int32_t offset; - - error_attr = (ietf_attr_pa_tnc_error_t*)attr; - error_code = error_attr->get_error_code(error_attr); - msg_info = error_attr->get_msg_info(error_attr); - DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B", - pa_tnc_error_code_names, error_code, &msg_info); - - switch (error_code) - { - case PA_ERROR_INVALID_PARAMETER: - offset = error_attr->get_offset(error_attr); - DBG1(DBG_IMV, " occurred at offset of %u bytes", offset); - break; - case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info); - break; - default: - break; - } - fatal_error = TRUE; - } - else if (attr->get_type(attr) == IETF_ATTR_PORT_FILTER) + if (attr->get_vendor_id(attr) == PEN_IETF && + attr->get_type(attr) == IETF_ATTR_PORT_FILTER) { ietf_attr_port_filter_t *attr_port_filter; enumerator_t *enumerator; @@ -339,10 +309,47 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, } return imv_scanner->provide_recommendation(imv_scanner, connection_id); + } + +/** + * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + + msg_vid = msg_type >> 8; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + + return receive_message(imv_id, connection_id, 0, chunk_create(msg, msg_len), + msg_vid, msg_subtype, 0, TNC_IMVID_ANY); +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) +{ + return receive_message(imv_id, connection_id, msg_flags, + chunk_create(msg, msg_len), msg_vid, msg_subtype, + src_imc_id, dst_imv_id); } /** - * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, TNC_ConnectionID connection_id) @@ -356,7 +363,7 @@ TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, } /** - * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, TNC_ConnectionID connection_id) @@ -370,7 +377,7 @@ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, } /** - * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) { @@ -388,7 +395,7 @@ TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) } /** - * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2 + * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id, TNC_TNCS_BindFunctionPointer bind_function) diff --git a/src/libimcv/plugins/imv_scanner/imv_scanner_state.c b/src/libimcv/plugins/imv_scanner/imv_scanner_state.c index a9b048bcb..422cb980d 100644 --- a/src/libimcv/plugins/imv_scanner/imv_scanner_state.c +++ b/src/libimcv/plugins/imv_scanner/imv_scanner_state.c @@ -40,6 +40,16 @@ struct private_imv_scanner_state_t { TNC_ConnectionState state; /** + * Does the TNCCS connection support long message types? + */ + bool has_long; + + /** + * Does the TNCCS connection support exclusive delivery? + */ + bool has_excl; + + /** * IMV action recommendation */ TNC_IMV_Action_Recommendation rec; @@ -86,6 +96,25 @@ METHOD(imv_state_t, get_connection_id, TNC_ConnectionID, return this->connection_id; } +METHOD(imv_state_t, has_long, bool, + private_imv_scanner_state_t *this) +{ + return this->has_long; +} + +METHOD(imv_state_t, has_excl, bool, + private_imv_scanner_state_t *this) +{ + return this->has_excl; +} + +METHOD(imv_state_t, set_flags, void, + private_imv_scanner_state_t *this, bool has_long, bool has_excl) +{ + this->has_long = has_long; + this->has_excl = has_excl; +} + METHOD(imv_state_t, change_state, void, private_imv_scanner_state_t *this, TNC_ConnectionState new_state) { @@ -191,6 +220,9 @@ imv_state_t *imv_scanner_state_create(TNC_ConnectionID connection_id) .public = { .interface = { .get_connection_id = _get_connection_id, + .has_long = _has_long, + .has_excl = _has_excl, + .set_flags = _set_flags, .change_state = _change_state, .get_recommendation = _get_recommendation, .set_recommendation = _set_recommendation, diff --git a/src/libimcv/plugins/imv_test/imv_test.c b/src/libimcv/plugins/imv_test/imv_test.c index 88db24983..0afd81aec 100644 --- a/src/libimcv/plugins/imv_test/imv_test.c +++ b/src/libimcv/plugins/imv_test/imv_test.c @@ -37,7 +37,7 @@ static const char imv_name[] = "Test"; static imv_agent_t *imv_test; /** - * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, TNC_Version min_version, @@ -64,16 +64,13 @@ TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, } /** - * see section 3.7.2 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, TNC_ConnectionID connection_id, TNC_ConnectionState new_state) { imv_state_t *state; - imv_test_state_t *test_state; - TNC_Result result; - int rounds; if (!imv_test) { @@ -87,60 +84,29 @@ TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, return imv_test->create_state(imv_test, state); case TNC_CONNECTION_STATE_DELETE: return imv_test->delete_state(imv_test, connection_id); - case TNC_CONNECTION_STATE_HANDSHAKE: - /* get updated IMV state */ - result = imv_test->change_state(imv_test, connection_id, - new_state, &state); - if (result != TNC_RESULT_SUCCESS) - { - return result; - } - test_state = (imv_test_state_t*)state; - - /* set the number of measurement rounds */ - rounds = lib->settings->get_int(lib->settings, - "libimcv.plugins.imv-test.rounds", 0); - test_state->set_rounds(test_state, rounds); - return TNC_RESULT_SUCCESS; default: return imv_test->change_state(imv_test, connection_id, new_state, NULL); } } -static TNC_Result send_message(TNC_ConnectionID connection_id) -{ - pa_tnc_msg_t *msg; - pa_tnc_attr_t *attr; - TNC_Result result; - - attr = ita_attr_command_create("repeat"); - msg = pa_tnc_msg_create(); - msg->add_attribute(msg, attr); - msg->build(msg); - result = imv_test->send_message(imv_test, connection_id, - msg->get_encoding(msg)); - msg->destroy(msg); - - return result; -} - -/** - * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2 - */ -TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, +static TNC_Result receive_message(TNC_IMVID imv_id, TNC_ConnectionID connection_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type) + TNC_UInt32 msg_flags, + chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) { pa_tnc_msg_t *pa_tnc_msg; pa_tnc_attr_t *attr; imv_state_t *state; - imv_test_state_t *imv_test_state; + imv_test_state_t *test_state; enumerator_t *enumerator; TNC_Result result; - bool fatal_error = FALSE, retry = FALSE; + int rounds; + bool fatal_error, retry = FALSE; if (!imv_test) { @@ -153,11 +119,11 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, { return TNC_RESULT_FATAL; } + test_state = (imv_test_state_t*)state; /* parse received PA-TNC message and automatically handle any errors */ - result = imv_test->receive_message(imv_test, connection_id, - chunk_create(msg, msg_len), msg_type, - &pa_tnc_msg); + result = imv_test->receive_message(imv_test, state, msg, msg_vid, + msg_subtype, src_imc_id, dst_imv_id, &pa_tnc_msg); /* no parsed PA-TNC attributes available if an error occurred */ if (!pa_tnc_msg) @@ -165,41 +131,20 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, return result; } + /* preprocess any IETF standard error attributes */ + fatal_error = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg); + + /* add any new IMC and set its number of rounds */ + rounds = lib->settings->get_int(lib->settings, + "libimcv.plugins.imv-test.rounds", 0); + test_state->add_imc(test_state, src_imc_id, rounds); + /* analyze PA-TNC attributes */ enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); while (enumerator->enumerate(enumerator, &attr)) { - if (attr->get_vendor_id(attr) == PEN_IETF && - attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) - { - ietf_attr_pa_tnc_error_t *error_attr; - pa_tnc_error_code_t error_code; - chunk_t msg_info, attr_info; - u_int32_t offset; - - error_attr = (ietf_attr_pa_tnc_error_t*)attr; - error_code = error_attr->get_error_code(error_attr); - msg_info = error_attr->get_msg_info(error_attr); - - DBG1(DBG_IMV, "received PA-TNC error '%N' concerning message %#B", - pa_tnc_error_code_names, error_code, &msg_info); - switch (error_code) - { - case PA_ERROR_INVALID_PARAMETER: - offset = error_attr->get_offset(error_attr); - DBG1(DBG_IMV, " occurred at offset of %u bytes", offset); - break; - case PA_ERROR_ATTR_TYPE_NOT_SUPPORTED: - attr_info = error_attr->get_attr_info(error_attr); - DBG1(DBG_IMV, " unsupported attribute %#B", &attr_info); - break; - default: - break; - } - fatal_error = TRUE; - } - else if (attr->get_vendor_id(attr) == PEN_ITA && - attr->get_type(attr) == ITA_ATTR_COMMAND) + if (attr->get_vendor_id(attr) == PEN_ITA && + attr->get_type(attr) == ITA_ATTR_COMMAND) { ita_attr_command_t *ita_attr; char *command; @@ -252,22 +197,67 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, /* request a handshake retry ? */ if (retry) { + test_state->set_rounds(test_state, rounds); return imv_test->request_handshake_retry(imv_id, connection_id, TNC_RETRY_REASON_IMV_SERIOUS_EVENT); } /* repeat the measurement ? */ - imv_test_state = (imv_test_state_t*)state; - if (imv_test_state->another_round(imv_test_state)) + if (test_state->another_round(test_state, src_imc_id)) { - return send_message(connection_id); + attr = ita_attr_command_create("repeat"); + pa_tnc_msg = pa_tnc_msg_create(); + pa_tnc_msg->add_attribute(pa_tnc_msg, attr); + pa_tnc_msg->build(pa_tnc_msg); + result = imv_test->send_message(imv_test, connection_id, TRUE, imv_id, + src_imc_id, pa_tnc_msg->get_encoding(pa_tnc_msg)); + pa_tnc_msg->destroy(pa_tnc_msg); + + return result; } return imv_test->provide_recommendation(imv_test, connection_id); } /** - * see section 3.7.4 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + + msg_vid = msg_type >> 8; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + + return receive_message(imv_id, connection_id, 0, chunk_create(msg, msg_len), + msg_vid, msg_subtype, 0, TNC_IMVID_ANY); +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) +{ + return receive_message(imv_id, connection_id, msg_flags, + chunk_create(msg, msg_len), msg_vid, msg_subtype, + src_imc_id, dst_imv_id); +} + +/** + * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, TNC_ConnectionID connection_id) @@ -281,7 +271,7 @@ TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, } /** - * see section 3.7.5 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, TNC_ConnectionID connection_id) @@ -295,7 +285,7 @@ TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, } /** - * see section 3.7.6 of TCG TNC IF-IMV Specification 1.2 + * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) { @@ -311,7 +301,7 @@ TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) } /** - * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.2 + * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3 */ TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id, TNC_TNCS_BindFunctionPointer bind_function) diff --git a/src/libimcv/plugins/imv_test/imv_test_state.c b/src/libimcv/plugins/imv_test/imv_test_state.c index 930da93e4..530090af7 100644 --- a/src/libimcv/plugins/imv_test/imv_test_state.c +++ b/src/libimcv/plugins/imv_test/imv_test_state.c @@ -15,6 +15,7 @@ #include "imv_test_state.h" #include <utils/lexparser.h> +#include <utils/linked_list.h> #include <debug.h> typedef struct private_imv_test_state_t private_imv_test_state_t; @@ -40,6 +41,16 @@ struct private_imv_test_state_t { TNC_ConnectionState state; /** + * Does the TNCCS connection support long message types? + */ + bool has_long; + + /** + * Does the TNCCS connection support exclusive delivery? + */ + bool has_excl; + + /** * IMV action recommendation */ TNC_IMV_Action_Recommendation rec; @@ -50,10 +61,20 @@ struct private_imv_test_state_t { TNC_IMV_Evaluation_Result eval; /** - * IMC-IMV round-trip count + * List of IMCs */ - int rounds; + linked_list_t *imcs; + +}; +typedef struct imc_entry_t imc_entry_t; + +/** + * Define an internal IMC entry + */ +struct imc_entry_t { + TNC_UInt32 imc_id; + int rounds; }; typedef struct entry_t entry_t; @@ -82,6 +103,25 @@ METHOD(imv_state_t, get_connection_id, TNC_ConnectionID, return this->connection_id; } +METHOD(imv_state_t, has_long, bool, + private_imv_test_state_t *this) +{ + return this->has_long; +} + +METHOD(imv_state_t, has_excl, bool, + private_imv_test_state_t *this) +{ + return this->has_excl; +} + +METHOD(imv_state_t, set_flags, void, + private_imv_test_state_t *this, bool has_long, bool has_excl) +{ + this->has_long = has_long; + this->has_excl = has_excl; +} + METHOD(imv_state_t, change_state, void, private_imv_test_state_t *this, TNC_ConnectionState new_state) { @@ -151,19 +191,73 @@ METHOD(imv_state_t, get_reason_string, bool, METHOD(imv_state_t, destroy, void, private_imv_test_state_t *this) { + this->imcs->destroy_function(this->imcs, free); free(this); } +METHOD(imv_test_state_t, add_imc, void, + private_imv_test_state_t *this, TNC_UInt32 imc_id, int rounds) +{ + enumerator_t *enumerator; + imc_entry_t *imc_entry; + bool found = FALSE; + + enumerator = this->imcs->create_enumerator(this->imcs); + while (enumerator->enumerate(enumerator, &imc_entry)) + { + if (imc_entry->imc_id == imc_id) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!found) + { + imc_entry = malloc_thing(imc_entry_t); + imc_entry->imc_id = imc_id; + imc_entry->rounds = rounds; + this->imcs->insert_last(this->imcs, imc_entry); + } +} + METHOD(imv_test_state_t, set_rounds, void, private_imv_test_state_t *this, int rounds) { - this->rounds = rounds; + enumerator_t *enumerator; + imc_entry_t *imc_entry; + + enumerator = this->imcs->create_enumerator(this->imcs); + while (enumerator->enumerate(enumerator, &imc_entry)) + { + imc_entry->rounds = rounds; + } + enumerator->destroy(enumerator); } METHOD(imv_test_state_t, another_round, bool, - private_imv_test_state_t *this) + private_imv_test_state_t *this, TNC_UInt32 imc_id) { - return (this->rounds-- > 0); + enumerator_t *enumerator; + imc_entry_t *imc_entry; + bool not_finished = FALSE; + + enumerator = this->imcs->create_enumerator(this->imcs); + while (enumerator->enumerate(enumerator, &imc_entry)) + { + if (imc_entry->rounds > 0) + { + not_finished = TRUE; + } + if (imc_entry->imc_id == imc_id) + { + imc_entry->rounds--; + } + } + enumerator->destroy(enumerator); + + return not_finished; } /** @@ -177,12 +271,16 @@ imv_state_t *imv_test_state_create(TNC_ConnectionID connection_id) .public = { .interface = { .get_connection_id = _get_connection_id, + .has_long = _has_long, + .has_excl = _has_excl, + .set_flags = _set_flags, .change_state = _change_state, .get_recommendation = _get_recommendation, .set_recommendation = _set_recommendation, .get_reason_string = _get_reason_string, .destroy = _destroy, }, + .add_imc = _add_imc, .set_rounds = _set_rounds, .another_round = _another_round, }, @@ -190,6 +288,7 @@ imv_state_t *imv_test_state_create(TNC_ConnectionID connection_id) .rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, .eval = TNC_IMV_EVALUATION_RESULT_DONT_KNOW, .connection_id = connection_id, + .imcs = linked_list_create(), ); return &this->public.interface; diff --git a/src/libimcv/plugins/imv_test/imv_test_state.h b/src/libimcv/plugins/imv_test/imv_test_state.h index 7e7b3a8f3..af78d1470 100644 --- a/src/libimcv/plugins/imv_test/imv_test_state.h +++ b/src/libimcv/plugins/imv_test/imv_test_state.h @@ -37,6 +37,14 @@ struct imv_test_state_t { imv_state_t interface; /** + * Add an IMC + * + * @param imc_id ID of the IMC to be added + * @param rounds number of re-measurement rounds + */ + void (*add_imc)(imv_test_state_t *this, TNC_UInt32 imc_id, int rounds); + + /** * Set the IMC-IMV round-trip count * * @param rounds number of re-measurement rounds @@ -46,9 +54,10 @@ struct imv_test_state_t { /** * Check and decrease IMC-IMV round-trip count * + * @param imc_id ID of the IMC to be checked * @return new connection state */ - bool (*another_round)(imv_test_state_t *this); + bool (*another_round)(imv_test_state_t *this, TNC_UInt32 imc_id); }; /** diff --git a/src/libpts/Makefile.am b/src/libpts/Makefile.am index ee729c287..3ff941794 100644 --- a/src/libpts/Makefile.am +++ b/src/libpts/Makefile.am @@ -3,29 +3,56 @@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libimcv ipseclib_LTLIBRARIES = libpts.la -libpts_la_LIBADD = -ltspi +libpts_la_LIBADD = $(top_builddir)/src/libimcv/libimcv.la -ltspi libpts_la_SOURCES = \ libpts.h libpts.c \ pts/pts.h pts/pts.c \ pts/pts_error.h pts/pts_error.c \ - pts/pts_proto_caps.h pts/pts_funct_comp_name.h pts/pts_file_type.h \ + pts/pts_proto_caps.h \ + pts/pts_req_func_comp_evid.h \ + pts/pts_simple_evid_final.h \ pts/pts_creds.h pts/pts_creds.c \ pts/pts_database.h pts/pts_database.c \ + pts/pts_dh_group.h pts/pts_dh_group.c \ pts/pts_file_meas.h pts/pts_file_meas.c \ pts/pts_file_meta.h pts/pts_file_meta.c \ + pts/pts_file_type.h pts/pts_file_type.c \ pts/pts_meas_algo.h pts/pts_meas_algo.c \ + pts/components/pts_component.h \ + pts/components/pts_component_manager.h pts/components/pts_component_manager.c \ + pts/components/pts_comp_evidence.h pts/components/pts_comp_evidence.c \ + pts/components/pts_comp_func_name.h pts/components/pts_comp_func_name.c \ + pts/components/ita/ita_comp_func_name.h pts/components/ita/ita_comp_func_name.c \ + pts/components/ita/ita_comp_ima.h pts/components/ita/ita_comp_ima.c \ + pts/components/ita/ita_comp_tboot.h pts/components/ita/ita_comp_tboot.c \ + pts/components/ita/ita_comp_tgrub.h pts/components/ita/ita_comp_tgrub.c \ + pts/components/tcg/tcg_comp_func_name.h pts/components/tcg/tcg_comp_func_name.c \ tcg/tcg_attr.h tcg/tcg_attr.c \ tcg/tcg_pts_attr_proto_caps.h tcg/tcg_pts_attr_proto_caps.c \ + tcg/tcg_pts_attr_dh_nonce_params_req.h tcg/tcg_pts_attr_dh_nonce_params_req.c \ + tcg/tcg_pts_attr_dh_nonce_params_resp.h tcg/tcg_pts_attr_dh_nonce_params_resp.c \ + tcg/tcg_pts_attr_dh_nonce_finish.h tcg/tcg_pts_attr_dh_nonce_finish.c \ tcg/tcg_pts_attr_meas_algo.h tcg/tcg_pts_attr_meas_algo.c \ tcg/tcg_pts_attr_get_tpm_version_info.h tcg/tcg_pts_attr_get_tpm_version_info.c \ tcg/tcg_pts_attr_tpm_version_info.h tcg/tcg_pts_attr_tpm_version_info.c \ tcg/tcg_pts_attr_get_aik.h tcg/tcg_pts_attr_get_aik.c \ tcg/tcg_pts_attr_aik.h tcg/tcg_pts_attr_aik.c \ - tcg/tcg_pts_attr_req_funct_comp_evid.h tcg/tcg_pts_attr_req_funct_comp_evid.c \ + tcg/tcg_pts_attr_req_func_comp_evid.h tcg/tcg_pts_attr_req_func_comp_evid.c \ tcg/tcg_pts_attr_gen_attest_evid.h tcg/tcg_pts_attr_gen_attest_evid.c \ tcg/tcg_pts_attr_simple_comp_evid.h tcg/tcg_pts_attr_simple_comp_evid.c \ tcg/tcg_pts_attr_simple_evid_final.h tcg/tcg_pts_attr_simple_evid_final.c \ tcg/tcg_pts_attr_req_file_meas.h tcg/tcg_pts_attr_req_file_meas.c \ tcg/tcg_pts_attr_file_meas.h tcg/tcg_pts_attr_file_meas.c \ + tcg/tcg_pts_attr_req_file_meta.h tcg/tcg_pts_attr_req_file_meta.c \ tcg/tcg_pts_attr_unix_file_meta.h tcg/tcg_pts_attr_unix_file_meta.c + +SUBDIRS = . + +if USE_IMC_ATTESTATION + SUBDIRS += plugins/imc_attestation +endif + +if USE_IMV_ATTESTATION + SUBDIRS += plugins/imv_attestation +endif diff --git a/src/libpts/libpts.c b/src/libpts/libpts.c index bd4c3a411..384ee4ed7 100644 --- a/src/libpts/libpts.c +++ b/src/libpts/libpts.c @@ -14,12 +14,23 @@ #include "libpts.h" #include "tcg/tcg_attr.h" +#include "pts/components/pts_component.h" +#include "pts/components/pts_component_manager.h" +#include "pts/components/tcg/tcg_comp_func_name.h" +#include "pts/components/ita/ita_comp_func_name.h" +#include "pts/components/ita/ita_comp_ima.h" +#include "pts/components/ita/ita_comp_tboot.h" +#include "pts/components/ita/ita_comp_tgrub.h" #include <imcv.h> - #include <debug.h> /** + * PTS Functional Component manager + */ +pts_component_manager_t *pts_components; + +/** * Reference count for IMC/IMV instances */ static refcount_t libpts_ref = 0; @@ -37,6 +48,25 @@ bool libpts_init(void) } imcv_pa_tnc_attributes->add_vendor(imcv_pa_tnc_attributes, PEN_TCG, tcg_attr_create_from_data, tcg_attr_names); + + pts_components = pts_component_manager_create(); + pts_components->add_vendor(pts_components, PEN_TCG, + pts_tcg_comp_func_names, PTS_TCG_QUALIFIER_TYPE_SIZE, + pts_tcg_qualifier_flag_names, pts_tcg_qualifier_type_names); + pts_components->add_vendor(pts_components, PEN_ITA, + pts_ita_comp_func_names, PTS_ITA_QUALIFIER_TYPE_SIZE, + pts_ita_qualifier_flag_names, pts_ita_qualifier_type_names); + + pts_components->add_component(pts_components, PEN_ITA, + PTS_ITA_COMP_FUNC_NAME_TGRUB, + pts_ita_comp_tgrub_create); + pts_components->add_component(pts_components, PEN_ITA, + PTS_ITA_COMP_FUNC_NAME_TBOOT, + pts_ita_comp_tboot_create); + pts_components->add_component(pts_components, PEN_ITA, + PTS_ITA_COMP_FUNC_NAME_IMA, + pts_ita_comp_ima_create); + DBG1(DBG_LIB, "libpts initialized"); } ref_get(&libpts_ref); @@ -51,6 +81,10 @@ void libpts_deinit(void) { if (ref_put(&libpts_ref)) { + pts_components->remove_vendor(pts_components, PEN_TCG); + pts_components->remove_vendor(pts_components, PEN_ITA); + pts_components->destroy(pts_components); + if (!imcv_pa_tnc_attributes) { return; diff --git a/src/libpts/libpts.h b/src/libpts/libpts.h index 4c771d236..7b2959728 100644 --- a/src/libpts/libpts.h +++ b/src/libpts/libpts.h @@ -25,6 +25,8 @@ #ifndef LIBPTS_H_ #define LIBPTS_H_ +#include "pts/components/pts_component_manager.h" + #include <library.h> /** @@ -39,4 +41,9 @@ bool libpts_init(void); */ void libpts_deinit(void); +/** + * PTS Functional Component manager + */ +extern pts_component_manager_t* pts_components; + #endif /** LIBPTS_H_ @}*/ diff --git a/src/libimcv/plugins/imc_attestation/Makefile.am b/src/libpts/plugins/imc_attestation/Makefile.am index ee082319d..9d78b935a 100644 --- a/src/libimcv/plugins/imc_attestation/Makefile.am +++ b/src/libpts/plugins/imc_attestation/Makefile.am @@ -11,7 +11,8 @@ imc_attestation_la_LIBADD = $(top_builddir)/src/libimcv/libimcv.la \ $(top_builddir)/src/libpts/libpts.la imc_attestation_la_SOURCES = imc_attestation.c \ - imc_attestation_state.h imc_attestation_state.c + imc_attestation_state.h imc_attestation_state.c \ + imc_attestation_process.h imc_attestation_process.c imc_attestation_la_LDFLAGS = -module -avoid-version diff --git a/src/libpts/plugins/imc_attestation/imc_attestation.c b/src/libpts/plugins/imc_attestation/imc_attestation.c new file mode 100644 index 000000000..4f77ba093 --- /dev/null +++ b/src/libpts/plugins/imc_attestation/imc_attestation.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "imc_attestation_state.h" +#include "imc_attestation_process.h" + +#include <imc/imc_agent.h> +#include <pa_tnc/pa_tnc_msg.h> +#include <ietf/ietf_attr.h> +#include <ietf/ietf_attr_pa_tnc_error.h> +#include <ietf/ietf_attr_product_info.h> + +#include <libpts.h> + +#include <pts/pts_error.h> + +#include <tcg/tcg_pts_attr_proto_caps.h> +#include <tcg/tcg_pts_attr_meas_algo.h> + +#include <tncif_pa_subtypes.h> + +#include <pen/pen.h> +#include <debug.h> +#include <utils/linked_list.h> + +/* IMC definitions */ + +static const char imc_name[] = "Attestation"; + +#define IMC_VENDOR_ID PEN_TCG +#define IMC_SUBTYPE PA_SUBTYPE_TCG_PTS + +static imc_agent_t *imc_attestation; + +/** + * Supported PTS measurement algorithms + */ +static pts_meas_algorithms_t supported_algorithms = PTS_MEAS_ALGO_NONE; + +/** + * Supported PTS Diffie Hellman Groups + */ +static pts_dh_group_t supported_dh_groups = PTS_DH_GROUP_NONE; + +/** + * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id, + TNC_Version min_version, + TNC_Version max_version, + TNC_Version *actual_version) +{ + if (imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name); + return TNC_RESULT_ALREADY_INITIALIZED; + } + if (!pts_meas_algo_probe(&supported_algorithms) || + !pts_dh_group_probe(&supported_dh_groups)) + { + return TNC_RESULT_FATAL; + } + imc_attestation = imc_agent_create(imc_name, IMC_VENDOR_ID, IMC_SUBTYPE, + imc_id, actual_version); + if (!imc_attestation) + { + return TNC_RESULT_FATAL; + } + + libpts_init(); + + if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1) + { + DBG1(DBG_IMC, "no common IF-IMC version"); + return TNC_RESULT_NO_COMMON_VERSION; + } + return TNC_RESULT_SUCCESS; +} + +/** + * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_ConnectionState new_state) +{ + imc_state_t *state; + + if (!imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); + return TNC_RESULT_NOT_INITIALIZED; + } + switch (new_state) + { + case TNC_CONNECTION_STATE_CREATE: + state = imc_attestation_state_create(connection_id); + return imc_attestation->create_state(imc_attestation, state); + case TNC_CONNECTION_STATE_DELETE: + return imc_attestation->delete_state(imc_attestation, connection_id); + case TNC_CONNECTION_STATE_HANDSHAKE: + case TNC_CONNECTION_STATE_ACCESS_ISOLATED: + case TNC_CONNECTION_STATE_ACCESS_NONE: + default: + return imc_attestation->change_state(imc_attestation, connection_id, + new_state, NULL); + } +} + + +/** + * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id, + TNC_ConnectionID connection_id) +{ + imc_state_t *state; + imc_attestation_state_t *attestation_state; + pts_t *pts; + char *platform_info; + TNC_Result result = TNC_RESULT_SUCCESS; + + if (!imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); + return TNC_RESULT_NOT_INITIALIZED; + } + + /* get current IMC state */ + if (!imc_attestation->get_state(imc_attestation, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + attestation_state = (imc_attestation_state_t*)state; + pts = attestation_state->get_pts(attestation_state); + + platform_info = pts->get_platform_info(pts); + if (platform_info) + { + pa_tnc_msg_t *pa_tnc_msg; + pa_tnc_attr_t *attr; + + pa_tnc_msg = pa_tnc_msg_create(); + attr = ietf_attr_product_info_create(0, 0, platform_info); + pa_tnc_msg->add_attribute(pa_tnc_msg, attr); + pa_tnc_msg->build(pa_tnc_msg); + result = imc_attestation->send_message(imc_attestation, connection_id, + FALSE, 0, TNC_IMVID_ANY, + pa_tnc_msg->get_encoding(pa_tnc_msg)); + pa_tnc_msg->destroy(pa_tnc_msg); + } + + return result; +} + +static TNC_Result receive_message(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) +{ + pa_tnc_msg_t *pa_tnc_msg; + pa_tnc_attr_t *attr; + linked_list_t *attr_list; + imc_state_t *state; + imc_attestation_state_t *attestation_state; + enumerator_t *enumerator; + TNC_Result result; + + if (!imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); + return TNC_RESULT_NOT_INITIALIZED; + } + + /* get current IMC state */ + if (!imc_attestation->get_state(imc_attestation, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + attestation_state = (imc_attestation_state_t*)state; + + /* parse received PA-TNC message and automatically handle any errors */ + result = imc_attestation->receive_message(imc_attestation, state, msg, + msg_vid, msg_subtype, src_imv_id, dst_imc_id, &pa_tnc_msg); + + /* no parsed PA-TNC attributes available if an error occurred */ + if (!pa_tnc_msg) + { + return result; + } + + /* preprocess any IETF standard error attributes */ + result = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg) ? + TNC_RESULT_FATAL : TNC_RESULT_SUCCESS; + + attr_list = linked_list_create(); + + /* analyze PA-TNC attributes */ + enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->get_vendor_id(attr) == PEN_IETF && + attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) + { + ietf_attr_pa_tnc_error_t *error_attr; + pen_t error_vendor_id; + pa_tnc_error_code_t error_code; + chunk_t msg_info; + + error_attr = (ietf_attr_pa_tnc_error_t*)attr; + error_vendor_id = error_attr->get_vendor_id(error_attr); + + if (error_vendor_id == PEN_TCG) + { + error_code = error_attr->get_error_code(error_attr); + msg_info = error_attr->get_msg_info(error_attr); + + DBG1(DBG_IMC, "received TCG-PTS error '%N'", + pts_error_code_names, error_code); + DBG1(DBG_IMC, "error information: %B", &msg_info); + + result = TNC_RESULT_FATAL; + } + } + else if (attr->get_vendor_id(attr) == PEN_TCG) + { + if (!imc_attestation_process(attr, attr_list, attestation_state, + supported_algorithms, supported_dh_groups)) + { + result = TNC_RESULT_FATAL; + break; + } + } + } + enumerator->destroy(enumerator); + pa_tnc_msg->destroy(pa_tnc_msg); + + if (result == TNC_RESULT_SUCCESS && attr_list->get_count(attr_list)) + { + pa_tnc_msg = pa_tnc_msg_create(); + + enumerator = attr_list->create_enumerator(attr_list); + while (enumerator->enumerate(enumerator, &attr)) + { + pa_tnc_msg->add_attribute(pa_tnc_msg, attr); + } + enumerator->destroy(enumerator); + + pa_tnc_msg->build(pa_tnc_msg); + result = imc_attestation->send_message(imc_attestation, connection_id, + FALSE, 0, TNC_IMVID_ANY, + pa_tnc_msg->get_encoding(pa_tnc_msg)); + pa_tnc_msg->destroy(pa_tnc_msg); + } + + attr_list->destroy(attr_list); + return result; +} + +/** + * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + + msg_vid = msg_type >> 8; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + + return receive_message(imc_id, connection_id, 0, chunk_create(msg, msg_len), + msg_vid, msg_subtype, 0, TNC_IMCID_ANY); +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMC_ReceiveMessageLong(TNC_IMCID imc_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id) +{ + return receive_message(imc_id, connection_id, msg_flags, + chunk_create(msg, msg_len), msg_vid, msg_subtype, + src_imv_id, dst_imc_id); +} + +/** + * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id, + TNC_ConnectionID connection_id) +{ + if (!imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); + return TNC_RESULT_NOT_INITIALIZED; + } + return TNC_RESULT_SUCCESS; +} + +/** + * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id) +{ + if (!imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); + return TNC_RESULT_NOT_INITIALIZED; + } + + libpts_deinit(); + + imc_attestation->destroy(imc_attestation); + imc_attestation = NULL; + + return TNC_RESULT_SUCCESS; +} + +/** + * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3 + */ +TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id, + TNC_TNCC_BindFunctionPointer bind_function) +{ + if (!imc_attestation) + { + DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name); + return TNC_RESULT_NOT_INITIALIZED; + } + return imc_attestation->bind_functions(imc_attestation, bind_function); +} diff --git a/src/libpts/plugins/imc_attestation/imc_attestation_process.c b/src/libpts/plugins/imc_attestation/imc_attestation_process.c new file mode 100644 index 000000000..b70c05370 --- /dev/null +++ b/src/libpts/plugins/imc_attestation/imc_attestation_process.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +/* for isdigit */ +#include <ctype.h> + +#include "imc_attestation_process.h" + +#include <ietf/ietf_attr_pa_tnc_error.h> + +#include <libpts.h> +#include <pts/pts.h> + +#include <tcg/tcg_pts_attr_proto_caps.h> +#include <tcg/tcg_pts_attr_meas_algo.h> +#include <tcg/tcg_pts_attr_dh_nonce_params_req.h> +#include <tcg/tcg_pts_attr_dh_nonce_params_resp.h> +#include <tcg/tcg_pts_attr_dh_nonce_finish.h> +#include <tcg/tcg_pts_attr_get_tpm_version_info.h> +#include <tcg/tcg_pts_attr_tpm_version_info.h> +#include <tcg/tcg_pts_attr_get_aik.h> +#include <tcg/tcg_pts_attr_aik.h> +#include <tcg/tcg_pts_attr_req_func_comp_evid.h> +#include <tcg/tcg_pts_attr_gen_attest_evid.h> +#include <tcg/tcg_pts_attr_simple_comp_evid.h> +#include <tcg/tcg_pts_attr_simple_evid_final.h> +#include <tcg/tcg_pts_attr_req_file_meas.h> +#include <tcg/tcg_pts_attr_file_meas.h> +#include <tcg/tcg_pts_attr_req_file_meta.h> +#include <tcg/tcg_pts_attr_unix_file_meta.h> + +#include <debug.h> +#include <utils/lexparser.h> + +#define DEFAULT_NONCE_LEN 20 + +bool imc_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, + imc_attestation_state_t *attestation_state, + pts_meas_algorithms_t supported_algorithms, + pts_dh_group_t supported_dh_groups) +{ + chunk_t attr_info; + pts_t *pts; + pts_error_code_t pts_error; + bool valid_path; + + pts = attestation_state->get_pts(attestation_state); + switch (attr->get_type(attr)) + { + case TCG_PTS_REQ_PROTO_CAPS: + { + tcg_pts_attr_proto_caps_t *attr_cast; + pts_proto_caps_flag_t imc_caps, imv_caps; + + attr_cast = (tcg_pts_attr_proto_caps_t*)attr; + imv_caps = attr_cast->get_flags(attr_cast); + imc_caps = pts->get_proto_caps(pts); + pts->set_proto_caps(pts, imc_caps & imv_caps); + + /* Send PTS Protocol Capabilities attribute */ + attr = tcg_pts_attr_proto_caps_create(imc_caps & imv_caps, FALSE); + attr_list->insert_last(attr_list, attr); + break; + } + case TCG_PTS_MEAS_ALGO: + { + tcg_pts_attr_meas_algo_t *attr_cast; + pts_meas_algorithms_t offered_algorithms, selected_algorithm; + + attr_cast = (tcg_pts_attr_meas_algo_t*)attr; + offered_algorithms = attr_cast->get_algorithms(attr_cast); + selected_algorithm = pts_meas_algo_select(supported_algorithms, + offered_algorithms); + if (selected_algorithm == PTS_MEAS_ALGO_NONE) + { + attr = pts_hash_alg_error_create(supported_algorithms); + attr_list->insert_last(attr_list, attr); + break; + } + + /* Send Measurement Algorithm Selection attribute */ + pts->set_meas_algorithm(pts, selected_algorithm); + attr = tcg_pts_attr_meas_algo_create(selected_algorithm, TRUE); + attr_list->insert_last(attr_list, attr); + break; + } + case TCG_PTS_DH_NONCE_PARAMS_REQ: + { + tcg_pts_attr_dh_nonce_params_req_t *attr_cast; + pts_dh_group_t offered_dh_groups, selected_dh_group; + chunk_t responder_value, responder_nonce; + int nonce_len, min_nonce_len; + + nonce_len = lib->settings->get_int(lib->settings, + "libimcv.plugins.imc-attestation.nonce_len", + DEFAULT_NONCE_LEN); + + attr_cast = (tcg_pts_attr_dh_nonce_params_req_t*)attr; + min_nonce_len = attr_cast->get_min_nonce_len(attr_cast); + if (nonce_len < PTS_MIN_NONCE_LEN || + (min_nonce_len > 0 && nonce_len < min_nonce_len)) + { + attr = pts_dh_nonce_error_create(nonce_len, PTS_MAX_NONCE_LEN); + attr_list->insert_last(attr_list, attr); + break; + } + + offered_dh_groups = attr_cast->get_dh_groups(attr_cast); + selected_dh_group = pts_dh_group_select(supported_dh_groups, + offered_dh_groups); + if (selected_dh_group == PTS_DH_GROUP_NONE) + { + attr = pts_dh_group_error_create(supported_dh_groups); + attr_list->insert_last(attr_list, attr); + break; + } + + /* Create own DH factor and nonce */ + if (!pts->create_dh_nonce(pts, selected_dh_group, nonce_len)) + { + return FALSE; + } + pts->get_my_public_value(pts, &responder_value, &responder_nonce); + + /* Send DH Nonce Parameters Response attribute */ + attr = tcg_pts_attr_dh_nonce_params_resp_create(selected_dh_group, + supported_algorithms, responder_nonce, responder_value); + attr_list->insert_last(attr_list, attr); + break; + } + case TCG_PTS_DH_NONCE_FINISH: + { + tcg_pts_attr_dh_nonce_finish_t *attr_cast; + pts_meas_algorithms_t selected_algorithm; + chunk_t initiator_nonce, initiator_value; + int nonce_len; + + attr_cast = (tcg_pts_attr_dh_nonce_finish_t*)attr; + selected_algorithm = attr_cast->get_hash_algo(attr_cast); + if (!(selected_algorithm & supported_algorithms)) + { + DBG1(DBG_IMC, "PTS-IMV selected unsupported DH hash algorithm"); + return FALSE; + } + pts->set_dh_hash_algorithm(pts, selected_algorithm); + + initiator_value = attr_cast->get_initiator_value(attr_cast); + initiator_nonce = attr_cast->get_initiator_nonce(attr_cast); + + nonce_len = lib->settings->get_int(lib->settings, + "libimcv.plugins.imc-attestation.nonce_len", + DEFAULT_NONCE_LEN); + if (nonce_len != initiator_nonce.len) + { + DBG1(DBG_IMC, "initiator and responder DH nonces " + "have differing lengths"); + return FALSE; + } + + pts->set_peer_public_value(pts, initiator_value, initiator_nonce); + if (!pts->calculate_secret(pts)) + { + return FALSE; + } + break; + } + case TCG_PTS_GET_TPM_VERSION_INFO: + { + chunk_t tpm_version_info, attr_info; + + if (!pts->get_tpm_version_info(pts, &tpm_version_info)) + { + attr_info = attr->get_value(attr); + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_TPM_VERS_NOT_SUPPORTED, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + + /* Send TPM Version Info attribute */ + attr = tcg_pts_attr_tpm_version_info_create(tpm_version_info); + attr_list->insert_last(attr_list, attr); + break; + } + case TCG_PTS_GET_AIK: + { + certificate_t *aik; + + aik = pts->get_aik(pts); + if (!aik) + { + DBG1(DBG_IMC, "no AIK certificate or public key available"); + break; + } + + /* Send AIK attribute */ + attr = tcg_pts_attr_aik_create(aik); + attr_list->insert_last(attr_list, attr); + break; + } + case TCG_PTS_REQ_FILE_MEAS: + { + tcg_pts_attr_req_file_meas_t *attr_cast; + char *pathname; + u_int16_t request_id; + bool is_directory; + u_int32_t delimiter; + pts_file_meas_t *measurements; + + attr_info = attr->get_value(attr); + attr_cast = (tcg_pts_attr_req_file_meas_t*)attr; + is_directory = attr_cast->get_directory_flag(attr_cast); + request_id = attr_cast->get_request_id(attr_cast); + delimiter = attr_cast->get_delimiter(attr_cast); + pathname = attr_cast->get_pathname(attr_cast); + valid_path = pts->is_path_valid(pts, pathname, &pts_error); + + if (valid_path && pts_error) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + pts_error, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + else if (!valid_path) + { + break; + } + + if (delimiter != SOLIDUS_UTF && delimiter != REVERSE_SOLIDUS_UTF) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_INVALID_DELIMITER, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + + /* Do PTS File Measurements and send them to PTS-IMV */ + DBG2(DBG_IMC, "measurement request %d for %s '%s'", + request_id, is_directory ? "directory" : "file", + pathname); + measurements = pts->do_measurements(pts, request_id, + pathname, is_directory); + if (!measurements) + { + /* TODO handle error codes from measurements */ + return FALSE; + } + attr = tcg_pts_attr_file_meas_create(measurements); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + break; + } + case TCG_PTS_REQ_FILE_META: + { + tcg_pts_attr_req_file_meta_t *attr_cast; + char *pathname; + bool is_directory; + u_int8_t delimiter; + pts_file_meta_t *metadata; + + attr_info = attr->get_value(attr); + attr_cast = (tcg_pts_attr_req_file_meta_t*)attr; + is_directory = attr_cast->get_directory_flag(attr_cast); + delimiter = attr_cast->get_delimiter(attr_cast); + pathname = attr_cast->get_pathname(attr_cast); + + valid_path = pts->is_path_valid(pts, pathname, &pts_error); + if (valid_path && pts_error) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + pts_error, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + else if (!valid_path) + { + break; + } + if (delimiter != SOLIDUS_UTF && delimiter != REVERSE_SOLIDUS_UTF) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_INVALID_DELIMITER, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + /* Get File Metadata and send them to PTS-IMV */ + DBG2(DBG_IMC, "metadata request for %s '%s'", + is_directory ? "directory" : "file", + pathname); + metadata = pts->get_metadata(pts, pathname, is_directory); + + if (!metadata) + { + /* TODO handle error codes from measurements */ + return FALSE; + } + attr = tcg_pts_attr_unix_file_meta_create(metadata); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + break; + } + case TCG_PTS_REQ_FUNC_COMP_EVID: + { + tcg_pts_attr_req_func_comp_evid_t *attr_cast; + pts_proto_caps_flag_t negotiated_caps; + pts_comp_func_name_t *name; + pts_comp_evidence_t *evid; + pts_component_t *comp; + u_int32_t depth; + u_int8_t flags; + status_t status; + enumerator_t *e; + + attr_info = attr->get_value(attr); + attr_cast = (tcg_pts_attr_req_func_comp_evid_t*)attr; + + DBG1(DBG_IMC, "evidence requested for %d functional components", + attr_cast->get_count(attr_cast)); + + e = attr_cast->create_enumerator(attr_cast); + while (e->enumerate(e, &flags, &depth, &name)) + { + name->log(name, "* "); + negotiated_caps = pts->get_proto_caps(pts); + + if (flags & PTS_REQ_FUNC_COMP_EVID_TTC) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_UNABLE_DET_TTC, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + if (flags & PTS_REQ_FUNC_COMP_EVID_VER && + !(negotiated_caps & PTS_PROTO_CAPS_V)) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_UNABLE_LOCAL_VAL, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + if (flags & PTS_REQ_FUNC_COMP_EVID_CURR && + !(negotiated_caps & PTS_PROTO_CAPS_C)) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_UNABLE_CUR_EVID, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + if (flags & PTS_REQ_FUNC_COMP_EVID_PCR && + !(negotiated_caps & PTS_PROTO_CAPS_T)) + { + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, + TCG_PTS_UNABLE_DET_PCR, attr_info); + attr_list->insert_last(attr_list, attr); + break; + } + if (depth > 0) + { + DBG1(DBG_IMC, "the Attestation IMC currently does not " + "support sub component measurements"); + return FALSE; + } + comp = pts_components->create(pts_components, name, depth, NULL); + if (!comp) + { + DBG2(DBG_IMC, " not registered: no evidence provided"); + continue; + } + + /* do the component evidence measurement[s] */ + do + { + status = comp->measure(comp, pts, &evid); + if (status == FAILED) + { + break; + } + attestation_state->add_evidence(attestation_state, evid); + } + while (status == NEED_MORE); + comp->destroy(comp); + } + e->destroy(e); + break; + } + case TCG_PTS_GEN_ATTEST_EVID: + { + pts_simple_evid_final_flag_t flags; + pts_meas_algorithms_t comp_hash_algorithm; + pts_comp_evidence_t *evid; + chunk_t pcr_composite, quote_sig; + bool use_quote2; + + /* Send buffered Simple Component Evidences */ + while (attestation_state->next_evidence(attestation_state, &evid)) + { + pts->select_pcr(pts, evid->get_extended_pcr(evid)); + + /* Send Simple Component Evidence */ + attr = tcg_pts_attr_simple_comp_evid_create(evid); + attr_list->insert_last(attr_list, attr); + } + + use_quote2 = lib->settings->get_bool(lib->settings, + "libimcv.plugins.imc-attestation.use_quote2", TRUE); + if (!pts->quote_tpm(pts, use_quote2, &pcr_composite, "e_sig)) + { + DBG1(DBG_IMC, "error occurred during TPM quote operation"); + return FALSE; + } + + /* Send Simple Evidence Final attribute */ + flags = use_quote2 ? PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 : + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO; + comp_hash_algorithm = PTS_MEAS_ALGO_SHA1; + + attr = tcg_pts_attr_simple_evid_final_create(flags, + comp_hash_algorithm, pcr_composite, quote_sig); + attr_list->insert_last(attr_list, attr); + break; + } + /* TODO: Not implemented yet */ + case TCG_PTS_REQ_INTEG_MEAS_LOG: + /* Attributes using XML */ + case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META: + case TCG_PTS_UPDATE_TEMPL_REF_MANI: + /* On Windows only*/ + case TCG_PTS_REQ_REGISTRY_VALUE: + /* Received on IMV side only*/ + case TCG_PTS_PROTO_CAPS: + case TCG_PTS_DH_NONCE_PARAMS_RESP: + case TCG_PTS_MEAS_ALGO_SELECTION: + case TCG_PTS_TPM_VERSION_INFO: + case TCG_PTS_TEMPL_REF_MANI_SET_META: + case TCG_PTS_AIK: + case TCG_PTS_SIMPLE_COMP_EVID: + case TCG_PTS_SIMPLE_EVID_FINAL: + case TCG_PTS_VERIFICATION_RESULT: + case TCG_PTS_INTEG_REPORT: + case TCG_PTS_UNIX_FILE_META: + case TCG_PTS_FILE_MEAS: + case TCG_PTS_INTEG_MEAS_LOG: + default: + DBG1(DBG_IMC, "received unsupported attribute '%N'", + tcg_attr_names, attr->get_type(attr)); + break; + } + return TRUE; +} diff --git a/src/libpts/plugins/imc_attestation/imc_attestation_process.h b/src/libpts/plugins/imc_attestation/imc_attestation_process.h new file mode 100644 index 000000000..b6dca1f56 --- /dev/null +++ b/src/libpts/plugins/imc_attestation/imc_attestation_process.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 imc_attestation_process_t imc_attestation_process + * @{ @ingroup imc_attestation_process + */ + +#ifndef IMC_ATTESTATION_PROCESS_H_ +#define IMC_ATTESTATION_PROCESS_H_ + +#include "imc_attestation_state.h" + +#include <library.h> + +#include <pa_tnc/pa_tnc_attr.h> + +#include <pts/pts_dh_group.h> +#include <pts/pts_meas_algo.h> + +/** + * Process a TCG PTS attribute + * + * @param attr PA-TNC attribute to be processed + * @param attr_list list with PA-TNC error attributes + * @param attestation_state attestation state of a given connection + * @param supported_algorithms supported PTS measurement algorithms + * @param supported_dh_groups supported DH groups + * @return TRUE if successful + */ +bool imc_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, + imc_attestation_state_t *attestation_state, + pts_meas_algorithms_t supported_algorithms, + pts_dh_group_t supported_dh_groups); + +#endif /** IMC_ATTESTATION_PROCESS_H_ @}*/ diff --git a/src/libimcv/plugins/imc_attestation/imc_attestation_state.c b/src/libpts/plugins/imc_attestation/imc_attestation_state.c index 9087b711c..72a55f60e 100644 --- a/src/libimcv/plugins/imc_attestation/imc_attestation_state.c +++ b/src/libpts/plugins/imc_attestation/imc_attestation_state.c @@ -15,6 +15,7 @@ #include "imc_attestation_state.h" +#include <utils/linked_list.h> #include <debug.h> typedef struct private_imc_attestation_state_t private_imc_attestation_state_t; @@ -40,10 +41,25 @@ struct private_imc_attestation_state_t { TNC_ConnectionState state; /** + * Does the TNCCS connection support long message types? + */ + bool has_long; + + /** + * Does the TNCCS connection support exclusive delivery? + */ + bool has_excl; + + /** * PTS object */ pts_t *pts; + /** + * PTS Component Evidence list + */ + linked_list_t *list; + }; METHOD(imc_state_t, get_connection_id, TNC_ConnectionID, @@ -52,16 +68,37 @@ METHOD(imc_state_t, get_connection_id, TNC_ConnectionID, return this->connection_id; } +METHOD(imc_state_t, has_long, bool, + private_imc_attestation_state_t *this) +{ + return this->has_long; +} + +METHOD(imc_state_t, has_excl, bool, + private_imc_attestation_state_t *this) +{ + return this->has_excl; +} + +METHOD(imc_state_t, set_flags, void, + private_imc_attestation_state_t *this, bool has_long, bool has_excl) +{ + this->has_long = has_long; + this->has_excl = has_excl; +} + METHOD(imc_state_t, change_state, void, private_imc_attestation_state_t *this, TNC_ConnectionState new_state) { this->state = new_state; } + METHOD(imc_state_t, destroy, void, private_imc_attestation_state_t *this) { this->pts->destroy(this->pts); + this->list->destroy_offset(this->list, offsetof(pts_comp_evidence_t, destroy)); free(this); } @@ -71,6 +108,18 @@ METHOD(imc_attestation_state_t, get_pts, pts_t*, return this->pts; } +METHOD(imc_attestation_state_t, add_evidence, void, + private_imc_attestation_state_t *this, pts_comp_evidence_t *evidence) +{ + this->list->insert_last(this->list, evidence); +} + +METHOD(imc_attestation_state_t, next_evidence, bool, + private_imc_attestation_state_t *this, pts_comp_evidence_t **evid) +{ + return this->list->remove_first(this->list, (void**)evid) == SUCCESS; +} + /** * Described in header. */ @@ -83,14 +132,20 @@ imc_state_t *imc_attestation_state_create(TNC_ConnectionID connection_id) .public = { .interface = { .get_connection_id = _get_connection_id, + .has_long = _has_long, + .has_excl = _has_excl, + .set_flags = _set_flags, .change_state = _change_state, .destroy = _destroy, }, .get_pts = _get_pts, + .add_evidence = _add_evidence, + .next_evidence = _next_evidence, }, .connection_id = connection_id, .state = TNC_CONNECTION_STATE_CREATE, .pts = pts_create(TRUE), + .list = linked_list_create(), ); platform_info = lib->settings->get_str(lib->settings, diff --git a/src/libimcv/plugins/imc_attestation/imc_attestation_state.h b/src/libpts/plugins/imc_attestation/imc_attestation_state.h index d083f3b57..22b0bba23 100644 --- a/src/libimcv/plugins/imc_attestation/imc_attestation_state.h +++ b/src/libpts/plugins/imc_attestation/imc_attestation_state.h @@ -24,6 +24,7 @@ #include <imc/imc_state.h> #include <pts/pts.h> +#include <pts/components/pts_comp_evidence.h> #include <library.h> typedef struct imc_attestation_state_t imc_attestation_state_t; @@ -45,6 +46,21 @@ struct imc_attestation_state_t { */ pts_t* (*get_pts)(imc_attestation_state_t *this); + /** + * Add an entry to the Component Evidence list + * + * @param entry Component Evidence entry + */ + void (*add_evidence)(imc_attestation_state_t *this, pts_comp_evidence_t *entry); + + /** + * Removes next Component Evidence entry from list and returns it + * + * @param evid Next Component Evidence entry + * @return TRUE if next entry is available + */ + bool (*next_evidence)(imc_attestation_state_t *this, pts_comp_evidence_t** evid); + }; /** diff --git a/src/libpts/plugins/imv_attestation/.gitignore b/src/libpts/plugins/imv_attestation/.gitignore new file mode 100644 index 000000000..79548ebac --- /dev/null +++ b/src/libpts/plugins/imv_attestation/.gitignore @@ -0,0 +1 @@ +attest diff --git a/src/libpts/plugins/imv_attestation/Makefile.am b/src/libpts/plugins/imv_attestation/Makefile.am new file mode 100644 index 000000000..a550a3552 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/Makefile.am @@ -0,0 +1,33 @@ + +INCLUDES = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libtncif \ + -I$(top_srcdir)/src/libimcv \ + -I$(top_srcdir)/src/libpts + +AM_CFLAGS = -rdynamic -DPLUGINS=\""${attest_plugins}\"" + +imcv_LTLIBRARIES = imv-attestation.la + +imv_attestation_la_LIBADD = \ + $(top_builddir)/src/libimcv/libimcv.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libpts/libpts.la + +imv_attestation_la_SOURCES = imv_attestation.c \ + imv_attestation_state.h imv_attestation_state.c \ + imv_attestation_process.h imv_attestation_process.c \ + imv_attestation_build.h imv_attestation_build.c + +imv_attestation_la_LDFLAGS = -module -avoid-version + +ipsec_PROGRAMS = attest +attest_SOURCES = attest.c \ + attest_usage.h attest_usage.c \ + attest_db.h attest_db.c \ + tables.sql data.sql +attest_LDADD = \ + $(top_builddir)/src/libimcv/libimcv.la \ + $(top_builddir)/src/libpts/libpts.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la +attest.o : $(top_builddir)/config.status diff --git a/src/libpts/plugins/imv_attestation/attest.c b/src/libpts/plugins/imv_attestation/attest.c new file mode 100644 index 000000000..9200820e8 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/attest.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2011 Andreas Steffen + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#define _GNU_SOURCE +#include <getopt.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> + +#include <library.h> +#include <debug.h> + +#include <imcv.h> +#include <libpts.h> +#include <pts/pts_meas_algo.h> + +#include "attest_db.h" +#include "attest_usage.h" + +/** + * global debug output variables + */ +static int debug_level = 2; +static bool stderr_quiet = TRUE; + +/** + * attest dbg function + */ +static void attest_dbg(debug_t group, level_t level, char *fmt, ...) +{ + int priority = LOG_INFO; + char buffer[8192]; + char *current = buffer, *next; + va_list args; + + if (level <= debug_level) + { + if (!stderr_quiet) + { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + } + + /* write in memory buffer first */ + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + /* do a syslog with every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + syslog(priority, "%s\n", current); + current = next; + } + } +} + +/** + * global attestation database object + */ +attest_db_t *attest; + +/** + * atexit handler to close db on shutdown + */ +static void cleanup(void) +{ + attest->destroy(attest); + libpts_deinit(); + libimcv_deinit(); + closelog(); +} + +static void do_args(int argc, char *argv[]) +{ + enum { + OP_UNDEF, + OP_USAGE, + OP_KEYS, + OP_COMPONENTS, + OP_FILES, + OP_HASHES, + OP_MEASUREMENTS, + OP_PRODUCTS, + OP_ADD, + OP_DEL, + } op = OP_UNDEF; + + /* reinit getopt state */ + optind = 0; + + while (TRUE) + { + int c; + + struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "components", no_argument, NULL, 'c' }, + { "files", no_argument, NULL, 'f' }, + { "keys", no_argument, NULL, 'k' }, + { "products", no_argument, NULL, 'p' }, + { "hashes", no_argument, NULL, 'H' }, + { "measurements", no_argument, NULL, 'm' }, + { "add", no_argument, NULL, 'a' }, + { "delete", no_argument, NULL, 'd' }, + { "del", no_argument, NULL, 'd' }, + { "aik", required_argument, NULL, 'A' }, + { "component", required_argument, NULL, 'C' }, + { "comp", required_argument, NULL, 'C' }, + { "directory", required_argument, NULL, 'D' }, + { "dir", required_argument, NULL, 'D' }, + { "file", required_argument, NULL, 'F' }, + { "key", required_argument, NULL, 'K' }, + { "owner", required_argument, NULL, 'O' }, + { "product", required_argument, NULL, 'P' }, + { "sha1", no_argument, NULL, '1' }, + { "sha256", no_argument, NULL, '2' }, + { "sha384", no_argument, NULL, '3' }, + { "did", required_argument, NULL, '4' }, + { "fid", required_argument, NULL, '5' }, + { "pid", required_argument, NULL, '6' }, + { "cid", required_argument, NULL, '7' }, + { "kid", required_argument, NULL, '8' }, + { 0,0,0,0 } + }; + + c = getopt_long(argc, argv, "", long_opts, NULL); + switch (c) + { + case EOF: + break; + case 'h': + op = OP_USAGE; + break; + case 'c': + op = OP_COMPONENTS; + continue; + case 'f': + op = OP_FILES; + continue; + case 'k': + op = OP_KEYS; + continue; + case 'p': + op = OP_PRODUCTS; + continue; + case 'H': + op = OP_HASHES; + continue; + case 'm': + op = OP_MEASUREMENTS; + continue; + case 'a': + op = OP_ADD; + continue; + case 'd': + op = OP_DEL; + continue; + case 'A': + { + certificate_t *aik_cert; + public_key_t *aik_key; + chunk_t aik; + + aik_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, + CERT_X509, BUILD_FROM_FILE, optarg, BUILD_END); + if (!aik_cert) + { + printf("AIK certificate '%s' could not be loaded\n", optarg); + exit(EXIT_FAILURE); + } + aik_key = aik_cert->get_public_key(aik_cert); + aik_cert->destroy(aik_cert); + + if (!aik_key) + { + printf("AIK public key could not be retrieved\n"); + exit(EXIT_FAILURE); + } + if (!aik_key->get_fingerprint(aik_key, KEYID_PUBKEY_INFO_SHA1, + &aik)) + { + printf("AIK fingerprint could not be computed\n"); + aik_key->destroy(aik_key); + exit(EXIT_FAILURE); + } + aik = chunk_clone(aik); + aik_key->destroy(aik_key); + + if (!attest->set_key(attest, aik, op == OP_ADD)) + { + exit(EXIT_FAILURE); + } + continue; + } + case 'C': + if (!attest->set_component(attest, optarg, op == OP_ADD)) + { + exit(EXIT_FAILURE); + } + continue; + case 'D': + if (!attest->set_directory(attest, optarg, op == OP_ADD)) + { + exit(EXIT_FAILURE); + } + continue; + case 'F': + if (!attest->set_file(attest, optarg, op == OP_ADD)) + { + exit(EXIT_FAILURE); + } + continue; + case 'K': + { + chunk_t aik; + + aik = chunk_from_hex(chunk_create(optarg, strlen(optarg)), NULL); + if (!attest->set_key(attest, aik, op == OP_ADD)) + { + exit(EXIT_FAILURE); + } + continue; + } + case 'O': + attest->set_owner(attest, optarg); + continue; + case 'P': + if (!attest->set_product(attest, optarg, op == OP_ADD)) + { + exit(EXIT_FAILURE); + } + continue; + case '1': + attest->set_algo(attest, PTS_MEAS_ALGO_SHA1); + continue; + case '2': + attest->set_algo(attest, PTS_MEAS_ALGO_SHA256); + continue; + case '3': + attest->set_algo(attest, PTS_MEAS_ALGO_SHA384); + continue; + case '4': + if (!attest->set_did(attest, atoi(optarg))) + { + exit(EXIT_FAILURE); + } + continue; + case '5': + if (!attest->set_fid(attest, atoi(optarg))) + { + exit(EXIT_FAILURE); + } + continue; + case '6': + if (!attest->set_pid(attest, atoi(optarg))) + { + exit(EXIT_FAILURE); + } + continue; + case '7': + if (!attest->set_cid(attest, atoi(optarg))) + { + exit(EXIT_FAILURE); + } + continue; + case '8': + if (!attest->set_kid(attest, atoi(optarg))) + { + exit(EXIT_FAILURE); + } + continue; + } + break; + } + + switch (op) + { + case OP_USAGE: + usage(); + break; + case OP_PRODUCTS: + attest->list_products(attest); + break; + case OP_KEYS: + attest->list_keys(attest); + break; + case OP_COMPONENTS: + attest->list_components(attest); + break; + case OP_FILES: + attest->list_files(attest); + break; + case OP_HASHES: + attest->list_hashes(attest); + break; + case OP_MEASUREMENTS: + attest->list_measurements(attest); + break; + case OP_ADD: + attest->add(attest); + break; + case OP_DEL: + attest->delete(attest); + break; + default: + usage(); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ + char *uri; + + /* enable attest debugging hook */ + dbg = attest_dbg; + openlog("attest", 0, LOG_DEBUG); + + atexit(library_deinit); + + /* initialize library */ + if (!library_init(NULL)) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (!lib->plugins->load(lib->plugins, NULL, + lib->settings->get_str(lib->settings, "attest.load", PLUGINS))) + { + exit(SS_RC_INITIALIZATION_FAILED); + } + + uri = lib->settings->get_str(lib->settings, "attest.database", NULL); + if (!uri) + { + fprintf(stderr, "database URI attest.database not set.\n"); + exit(SS_RC_INITIALIZATION_FAILED); + } + attest = attest_db_create(uri); + if (!attest) + { + exit(SS_RC_INITIALIZATION_FAILED); + } + atexit(cleanup); + libimcv_init(); + libpts_init(); + + do_args(argc, argv); + + exit(EXIT_SUCCESS); +} + diff --git a/src/libpts/plugins/imv_attestation/attest_db.c b/src/libpts/plugins/imv_attestation/attest_db.c new file mode 100644 index 000000000..88d19eee1 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/attest_db.c @@ -0,0 +1,1200 @@ +/* + * Copyright (C) 2011 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 "attest_db.h" + +#include "libpts.h" +#include "pts/components/pts_comp_func_name.h" + +typedef struct private_attest_db_t private_attest_db_t; + +/** + * Private data of an attest_db_t object. + */ +struct private_attest_db_t { + + /** + * Public members of attest_db_state_t + */ + attest_db_t public; + + /** + * Component Functional Name to be queried + */ + pts_comp_func_name_t *cfn; + + /** + * Primary key of the Component Functional Name to be queried + */ + int cid; + + /** + * TRUE if Component Functional Name has been set + */ + bool comp_set; + + /** + * Directory containing the Measurement file to be queried + */ + char *dir; + + /** + * Primary key of the directory to be queried + */ + int did; + + /** + * TRUE if directory has been set + */ + bool dir_set; + + /** + * Measurement file to be queried + */ + char *file; + + /** + * Primary key of measurement file to be queried + */ + int fid; + + /** + * TRUE if file has been set + */ + bool file_set; + + /** + * AIK to be queried + */ + chunk_t key; + + /** + * Primary key of the AIK to be queried + */ + int kid; + + /** + * TRUE if AIK has been set + */ + bool key_set; + + /** + * Software product to be queried + */ + char *product; + + /** + * Primary key of software product to be queried + */ + int pid; + + /** + * TRUE if product has been set + */ + bool product_set; + + /** + * File measurement hash algorithm + */ + pts_meas_algorithms_t algo; + + /** + * Optional owner (user/host name) + */ + char *owner; + + /** + * Attestation database + */ + database_t *db; + +}; + +char* print_cfn(pts_comp_func_name_t *cfn) +{ + static char buf[BUF_LEN]; + char flags[8]; + int type, vid, name, qualifier, n; + enum_name_t *names, *types; + + vid = cfn->get_vendor_id(cfn), + name = cfn->get_name(cfn); + qualifier = cfn->get_qualifier(cfn); + n = snprintf(buf, BUF_LEN, "0x%06x/0x%08x-0x%02x", vid, name, qualifier); + + names = pts_components->get_comp_func_names(pts_components, vid); + types = pts_components->get_qualifier_type_names(pts_components, vid); + type = pts_components->get_qualifier(pts_components, cfn, flags); + if (names && types) + { + n = snprintf(buf + n, BUF_LEN - n, " %N/%N [%s] %N", + pen_names, vid, names, name, flags, types, type); + } + return buf; +} + +METHOD(attest_db_t, set_component, bool, + private_attest_db_t *this, char *comp, bool create) +{ + enumerator_t *e; + char *pos1, *pos2; + int vid, name, qualifier; + pts_comp_func_name_t *cfn; + + if (this->comp_set) + { + printf("component has already been set\n"); + return FALSE; + } + + /* parse component string */ + pos1 = strchr(comp, '/'); + pos2 = strchr(comp, '-'); + if (!pos1 || !pos2) + { + printf("component string must have the form \"vendor_id/name-qualifier\"\n"); + return FALSE; + } + vid = atoi(comp); + name = atoi(pos1 + 1); + qualifier = atoi(pos2 + 1); + cfn = pts_comp_func_name_create(vid, name, qualifier); + + e = this->db->query(this->db, + "SELECT id FROM components " + "WHERE vendor_id = ? AND name = ? AND qualifier = ?", + DB_INT, vid, DB_INT, name, DB_INT, qualifier, DB_INT); + if (e) + { + if (e->enumerate(e, &this->cid)) + { + this->comp_set = TRUE; + this->cfn = cfn; + } + e->destroy(e); + } + if (this->comp_set) + { + return TRUE; + } + + if (!create) + { + printf("component '%s' not found in database\n", print_cfn(cfn)); + cfn->destroy(cfn); + return FALSE; + } + + /* Add a new database entry */ + this->comp_set = this->db->execute(this->db, &this->cid, + "INSERT INTO components (vendor_id, name, qualifier) " + "VALUES (?, ?, ?)", + DB_INT, vid, DB_INT, name, DB_INT, qualifier) == 1; + + printf("component '%s' %sinserted into database\n", print_cfn(cfn), + this->comp_set ? "" : "could not be "); + if (this->comp_set) + { + this->cfn = cfn; + } + else + { + cfn->destroy(cfn); + } + return this->comp_set; +} + +METHOD(attest_db_t, set_cid, bool, + private_attest_db_t *this, int cid) +{ + enumerator_t *e; + int vid, name, qualifier; + + if (this->comp_set) + { + printf("component has already been set\n"); + return FALSE; + } + this->cid = cid; + + e = this->db->query(this->db, "SELECT vendor_id, name, qualifier " + "FROM components WHERE id = ?", + DB_INT, cid, DB_INT, DB_INT, DB_INT); + if (e) + { + if (e->enumerate(e, &vid, &name, &qualifier)) + { + this->cfn = pts_comp_func_name_create(vid, name, qualifier); + this->comp_set = TRUE; + } + else + { + printf("no component found with cid %d\n", cid); + } + e->destroy(e); + } + return this->comp_set; +} + +METHOD(attest_db_t, set_directory, bool, + private_attest_db_t *this, char *dir, bool create) +{ + enumerator_t *e; + + if (this->dir_set) + { + printf("directory has already been set\n"); + return FALSE; + } + free(this->dir); + this->dir = strdup(dir); + + e = this->db->query(this->db, + "SELECT id FROM files WHERE type = 1 AND path = ?", + DB_TEXT, dir, DB_INT); + if (e) + { + if (e->enumerate(e, &this->did)) + { + this->dir_set = TRUE; + } + e->destroy(e); + } + if (this->dir_set) + { + return TRUE; + } + + if (!create) + { + printf("directory '%s' not found in database\n", dir); + return FALSE; + } + + /* Add a new database entry */ + this->dir_set = this->db->execute(this->db, &this->did, + "INSERT INTO files (type, path) VALUES (1, ?)", + DB_TEXT, dir) == 1; + + printf("directory '%s' %sinserted into database\n", dir, + this->dir_set ? "" : "could not be "); + + return this->dir_set; +} + +METHOD(attest_db_t, set_did, bool, + private_attest_db_t *this, int did) +{ + enumerator_t *e; + char *dir; + + if (this->dir_set) + { + printf("directory has already been set\n"); + return FALSE; + } + this->did = did; + + e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?", + DB_INT, did, DB_TEXT); + if (e) + { + if (e->enumerate(e, &dir)) + { + free(this->dir); + this->dir = strdup(dir); + this->dir_set = TRUE; + } + else + { + printf("no directory found with did %d\n", did); + } + e->destroy(e); + } + return this->dir_set; +} + +METHOD(attest_db_t, set_file, bool, + private_attest_db_t *this, char *file, bool create) +{ + enumerator_t *e; + + if (this->file_set) + { + printf("file has already been set\n"); + return FALSE; + } + this->file = strdup(file); + + e = this->db->query(this->db, "SELECT id FROM files WHERE path = ?", + DB_TEXT, file, DB_INT); + if (e) + { + if (e->enumerate(e, &this->fid)) + { + this->file_set = TRUE; + } + e->destroy(e); + } + if (this->file_set) + { + return TRUE; + } + + if (!create) + { + printf("file '%s' not found in database\n", file); + return FALSE; + } + + /* Add a new database entry */ + this->file_set = this->db->execute(this->db, &this->fid, + "INSERT INTO files (type, path) VALUES (0, ?)", + DB_TEXT, file) == 1; + + printf("file '%s' %sinserted into database\n", file, + this->file_set ? "" : "could not be "); + + return this->file_set; +} + +METHOD(attest_db_t, set_fid, bool, + private_attest_db_t *this, int fid) +{ + enumerator_t *e; + char *file; + + if (this->file_set) + { + printf("file has already been set\n"); + return FALSE; + } + this->fid = fid; + + e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?", + DB_INT, fid, DB_TEXT); + if (e) + { + if (e->enumerate(e, &file)) + { + this->file = strdup(file); + this->file_set = TRUE; + } + else + { + printf("no file found with fid %d\n", fid); + } + e->destroy(e); + } + return this->file_set; +} + +METHOD(attest_db_t, set_key, bool, + private_attest_db_t *this, chunk_t key, bool create) +{ + enumerator_t *e; + char *owner; + + if (this->key_set) + { + printf("key has already been set\n"); + return FALSE; + } + this->key = key; + + e = this->db->query(this->db, "SELECT id, owner FROM keys WHERE keyid= ?", + DB_BLOB, this->key, DB_INT, DB_TEXT); + if (e) + { + if (e->enumerate(e, &this->kid, &owner)) + { + free(this->owner); + this->owner = strdup(owner); + this->key_set = TRUE; + } + e->destroy(e); + } + if (this->key_set) + { + return TRUE; + } + + if (!create) + { + printf("key '%#B' not found in database\n", &this->key); + return FALSE; + } + + /* Add a new database entry */ + if (!this->owner) + { + this->owner = strdup(""); + } + this->key_set = this->db->execute(this->db, &this->kid, + "INSERT INTO keys (keyid, owner) VALUES (?, ?)", + DB_BLOB, this->key, DB_TEXT, this->owner) == 1; + + printf("key '%#B' %sinserted into database\n", &this->key, + this->key_set ? "" : "could not be "); + + return this->key_set; + +}; + +METHOD(attest_db_t, set_kid, bool, + private_attest_db_t *this, int kid) +{ + enumerator_t *e; + chunk_t key; + char *owner; + + if (this->key_set) + { + printf("key has already been set\n"); + return FALSE; + } + this->kid = kid; + + e = this->db->query(this->db, "SELECT keyid, owner FROM keys WHERE id = ?", + DB_INT, kid, DB_BLOB, DB_TEXT); + if (e) + { + if (e->enumerate(e, &key, &owner)) + { + this->owner = strdup(owner); + this->key = chunk_clone(key); + this->key_set = TRUE; + } + else + { + printf("no key found with kid %d\n", kid); + } + e->destroy(e); + } + return this->key_set; + +}; + +METHOD(attest_db_t, set_product, bool, + private_attest_db_t *this, char *product, bool create) +{ + enumerator_t *e; + + if (this->product_set) + { + printf("product has already been set\n"); + return FALSE; + } + this->product = strdup(product); + + e = this->db->query(this->db, "SELECT id FROM products WHERE name = ?", + DB_TEXT, product, DB_INT); + if (e) + { + if (e->enumerate(e, &this->pid)) + { + this->product_set = TRUE; + } + e->destroy(e); + } + if (this->product_set) + { + return TRUE; + } + + if (!create) + { + printf("product '%s' not found in database\n", product); + return FALSE; + } + + /* Add a new database entry */ + this->product_set = this->db->execute(this->db, &this->pid, + "INSERT INTO products (name) VALUES (?)", + DB_TEXT, product) == 1; + + printf("product '%s' %sinserted into database\n", product, + this->product_set ? "" : "could not be "); + + return this->product_set; +} + +METHOD(attest_db_t, set_pid, bool, + private_attest_db_t *this, int pid) +{ + enumerator_t *e; + char *product; + + if (this->product_set) + { + printf("product has already been set\n"); + return FALSE; + } + this->pid = pid; + + e = this->db->query(this->db, "SELECT name FROM products WHERE id = ?", + DB_INT, pid, DB_TEXT); + if (e) + { + if (e->enumerate(e, &product)) + { + this->product = strdup(product); + this->product_set = TRUE; + } + else + { + printf("no product found with pid %d in database\n", pid); + } + e->destroy(e); + } + return this->product_set; +} + +METHOD(attest_db_t, set_algo, void, + private_attest_db_t *this, pts_meas_algorithms_t algo) +{ + this->algo = algo; +} + +METHOD(attest_db_t, set_owner, void, + private_attest_db_t *this, char *owner) +{ + free(this->owner); + this->owner = strdup(owner); +} + +METHOD(attest_db_t, list_components, void, + private_attest_db_t *this) +{ + enumerator_t *e; + pts_comp_func_name_t *cfn; + int cid, vid, name, qualifier, count = 0; + + if (this->kid) + { + e = this->db->query(this->db, + "SELECT c.id, c.vendor_id, c.name, c.qualifier " + "FROM components AS c " + "JOIN key_component AS kc ON c.id = kc.component " + "WHERE kc.key = ? ORDER BY c.vendor_id, c.name, c.qualifier", + DB_INT, this->kid, DB_INT, DB_INT, DB_INT, DB_INT); + } + else + { + e = this->db->query(this->db, + "SELECT id, vendor_id, name, qualifier FROM components " + "ORDER BY vendor_id, name, qualifier", + DB_INT, DB_INT, DB_INT, DB_INT); + } + if (e) + { + while (e->enumerate(e, &cid, &vid, &name, &qualifier)) + { + cfn = pts_comp_func_name_create(vid, name, qualifier); + printf("%3d: %s\n", cid, print_cfn(cfn)); + cfn->destroy(cfn); + count++; + } + e->destroy(e); + + printf("%d component%s found", count, (count == 1) ? "" : "s"); + if (this->key_set) + { + printf(" for key %#B", &this->key); + } + printf("\n"); + } +} + +METHOD(attest_db_t, list_keys, void, + private_attest_db_t *this) +{ + enumerator_t *e; + chunk_t keyid; + char *owner; + int kid, count = 0; + + if (this->cid) + { + e = this->db->query(this->db, + "SELECT k.id, k.keyid, k.owner FROM keys AS k " + "JOIN key_component AS kc ON k.id = kc.key " + "WHERE kc.component = ? ORDER BY k.keyid", + DB_INT, this->cid, DB_INT, DB_BLOB, DB_TEXT); + if (e) + { + while (e->enumerate(e, &kid, &keyid, &owner)) + { + printf("%3d: %#B '%s'\n", kid, &keyid, owner); + count++; + } + e->destroy(e); + } + } + else + { + e = this->db->query(this->db, "SELECT id, keyid, owner FROM keys " + "ORDER BY keyid", + DB_INT, DB_BLOB, DB_TEXT); + if (e) + { + while (e->enumerate(e, &kid, &keyid, &owner)) + { + printf("%3d: %#B '%s'\n", kid, &keyid, owner); + count++; + } + e->destroy(e); + } + } + + printf("%d key%s found", count, (count == 1) ? "" : "s"); + if (this->comp_set) + { + printf(" for component '%s'", print_cfn(this->cfn)); + } + printf("\n"); +} + +METHOD(attest_db_t, list_files, void, + private_attest_db_t *this) +{ + enumerator_t *e; + char *file, *file_type[] = { " ", "d", "r" }; + int fid, type, meas, meta, count = 0; + + if (this->pid) + { + e = this->db->query(this->db, + "SELECT f.id, f.type, f.path, pf.measurement, pf.metadata " + "FROM files AS f " + "JOIN product_file AS pf ON f.id = pf.file " + "WHERE pf.product = ? ORDER BY f.path", + DB_INT, this->pid, DB_INT, DB_INT, DB_TEXT, DB_INT, DB_INT); + if (e) + { + while (e->enumerate(e, &fid, &type, &file, &meas, &meta)) + { + type = (type < 0 || type > 2) ? 0 : type; + printf("%3d: |%s%s| %s %s\n", fid, meas ? "M":" ", meta ? "T":" ", + file_type[type], file); + count++; + } + e->destroy(e); + } + } + else + { + e = this->db->query(this->db, + "SELECT id, type, path FROM files " + "ORDER BY path", + DB_INT, DB_INT, DB_TEXT); + if (e) + { + while (e->enumerate(e, &fid, &type, &file)) + { + type = (type < 0 || type > 2) ? 0 : type; + printf("%3d: %s %s\n", fid, file_type[type], file); + count++; + } + e->destroy(e); + } + } + + printf("%d file%s found", count, (count == 1) ? "" : "s"); + if (this->product_set) + { + printf(" for product '%s'", this->product); + } + printf("\n"); +} + +METHOD(attest_db_t, list_products, void, + private_attest_db_t *this) +{ + enumerator_t *e; + char *product; + int pid, meas, meta, count = 0; + + if (this->fid) + { + e = this->db->query(this->db, + "SELECT p.id, p.name, pf.measurement, pf.metadata " + "FROM products AS p " + "JOIN product_file AS pf ON p.id = pf.product " + "WHERE pf.file = ? ORDER BY p.name", + DB_INT, this->fid, DB_INT, DB_TEXT, DB_INT, DB_INT); + if (e) + { + while (e->enumerate(e, &pid, &product, &meas, &meta)) + { + printf("%3d: |%s%s| %s\n", pid, meas ? "M":" ", meta ? "T":" ", + product); + count++; + } + e->destroy(e); + } + } + else + { + e = this->db->query(this->db, "SELECT id, name FROM products " + "ORDER BY name", + DB_INT, DB_TEXT); + if (e) + { + while (e->enumerate(e, &pid, &product)) + { + printf("%3d: %s\n", pid, product); + count++; + } + e->destroy(e); + } + } + + printf("%d product%s found", count, (count == 1) ? "" : "s"); + if (this->file_set) + { + printf(" for file '%s'", this->file); + } + printf("\n"); +} + +/** + * get the directory if there is one from the files tables + */ +static void get_directory(private_attest_db_t *this, int did, char **directory) +{ + enumerator_t *e; + char *dir; + + free(*directory); + *directory = strdup(""); + + if (did) + { + e = this->db->query(this->db, + "SELECT path from files WHERE id = ?", + DB_INT, did, DB_TEXT); + if (e) + { + if (e->enumerate(e, &dir)) + { + free(*directory); + *directory = strdup(dir); + } + e->destroy(e); + } + } +} + +static bool slash(char *directory, char *file) +{ + return *file != '/' && directory[max(0, strlen(directory)-1)] != '/'; +} + +METHOD(attest_db_t, list_hashes, void, + private_attest_db_t *this) +{ + enumerator_t *e; + chunk_t hash; + char *file, *dir, *product; + int fid, fid_old = 0, did, did_old = 0, count = 0; + + dir = strdup(""); + + if (this->pid && this->fid) + { + e = this->db->query(this->db, + "SELECT hash FROM file_hashes " + "WHERE algo = ? AND file = ? AND directory = ? AND product = ?", + DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did, + DB_INT, this->pid, DB_BLOB); + if (e) + { + while (e->enumerate(e, &hash)) + { + if (this->fid != fid_old) + { + printf("%3d: %s%s%s\n", this->fid, this->dir, + slash(this->dir, this->file) ? "/" : "", this->file); + fid_old = this->fid; + } + printf(" %#B\n", &hash); + count++; + } + e->destroy(e); + + printf("%d %N value%s found for product '%s'\n", count, + hash_algorithm_names, pts_meas_algo_to_hash(this->algo), + (count == 1) ? "" : "s", this->product); + } + } + else if (this->pid) + { + e = this->db->query(this->db, + "SELECT f.id, f. f.path, fh.hash, fh.directory " + "FROM file_hashes AS fh " + "JOIN files AS f ON f.id = fh.file " + "WHERE fh.algo = ? AND fh.product = ? " + "ORDER BY fh.directory, f.path", + DB_INT, this->algo, DB_INT, this->pid, + DB_INT, DB_TEXT, DB_BLOB, DB_INT); + if (e) + { + while (e->enumerate(e, &fid, &file, &hash, &did)) + { + if (fid != fid_old || did != did_old) + { + if (did != did_old) + { + get_directory(this, did, &dir); + } + printf("%3d: %s%s%s\n", fid, + dir, slash(dir, file) ? "/" : "", file); + fid_old = fid; + did_old = did; + } + printf(" %#B\n", &hash); + count++; + } + e->destroy(e); + + printf("%d %N value%s found for product '%s'\n", count, + hash_algorithm_names, pts_meas_algo_to_hash(this->algo), + (count == 1) ? "" : "s", this->product); + } + } + else if (this->fid) + { + e = this->db->query(this->db, + "SELECT p.name, fh.hash, fh.directory " + "FROM file_hashes AS fh " + "JOIN products AS p ON p.id = fh.product " + "WHERE fh.algo = ? AND fh.file = ? AND fh.directory = ?" + "ORDER BY p.name", + DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did, + DB_TEXT, DB_BLOB, DB_INT); + if (e) + { + while (e->enumerate(e, &product, &hash, &did)) + { + printf("%#B '%s'\n", &hash, product); + count++; + } + e->destroy(e); + + printf("%d %N value%s found for file '%s%s%s'\n", + count, hash_algorithm_names, pts_meas_algo_to_hash(this->algo), + (count == 1) ? "" : "s", this->dir, + slash(this->dir, this->file) ? "/" : "", this->file); + } + } + else + { + e = this->db->query(this->db, + "SELECT f.id, f.path, p.name, fh.hash, fh.directory " + "FROM file_hashes AS fh " + "JOIN files AS f ON f.id = fh.file " + "JOIN products AS p ON p.id = fh.product " + "WHERE fh.algo = ? " + "ORDER BY fh.directory, f.path, p.name", + DB_INT, this->algo, + DB_INT, DB_TEXT, DB_TEXT, DB_BLOB, DB_INT); + if (e) + { + while (e->enumerate(e, &fid, &file, &product, &hash, &did)) + { + if (fid != fid_old || did != did_old) + { + if (did != did_old) + { + get_directory(this, did, &dir); + did_old = did; + } + printf("%3d: %s%s%s\n", fid, + dir, slash(dir, file) ? "/" : "", file); + fid_old = fid; + } + printf(" %#B '%s'\n", &hash, product); + count++; + } + e->destroy(e); + + printf("%d %N value%s found\n", count, hash_algorithm_names, + pts_meas_algo_to_hash(this->algo), (count == 1) ? "" : "s"); + } + } + free(dir); +} + +METHOD(attest_db_t, list_measurements, void, + private_attest_db_t *this) +{ + enumerator_t *e; + chunk_t hash, keyid; + pts_comp_func_name_t *cfn; + char *owner; + int seq_no, pcr, vid, name, qualifier; + int cid, cid_old = 0, kid, kid_old = 0, count = 0; + + if (this->kid && this->cid) + { + e = this->db->query(this->db, + "SELECT ch.seq_no, ch.pcr, ch.hash, k.owner " + "FROM component_hashes AS ch " + "JOIN keys AS k ON k.id = ch.key " + "WHERE ch.algo = ? AND ch.key = ? AND ch.component = ? " + "ORDER BY seq_no", + DB_INT, this->algo, DB_INT, this->kid, DB_INT, this->cid, + DB_INT, DB_INT, DB_BLOB, DB_TEXT); + if (e) + { + while (e->enumerate(e, &seq_no, &pcr, &hash, &owner)) + { + if (this->kid != kid_old) + { + printf("%3d: %#B '%s'\n", this->kid, &this->key, owner); + kid_old = this->kid; + } + printf("%5d %02d %#B\n", seq_no, pcr, &hash); + count++; + } + e->destroy(e); + + printf("%d %N value%s found for component '%s'\n", count, + hash_algorithm_names, pts_meas_algo_to_hash(this->algo), + (count == 1) ? "" : "s", print_cfn(this->cfn)); + } + } + else if (this->cid) + { + e = this->db->query(this->db, + "SELECT ch.seq_no, ch.pcr, ch.hash, k.id, k.keyid, k.owner " + "FROM component_hashes AS ch " + "JOIN keys AS k ON k.id = ch.key " + "WHERE ch.algo = ? AND ch.component = ? " + "ORDER BY keyid, seq_no", + DB_INT, this->algo, DB_INT, this->cid, + DB_INT, DB_INT, DB_BLOB, DB_INT, DB_BLOB, DB_TEXT); + if (e) + { + while (e->enumerate(e, &seq_no, &pcr, &hash, &kid, &keyid, &owner)) + { + if (kid != kid_old) + { + printf("%3d: %#B '%s'\n", kid, &keyid, owner); + kid_old = kid; + } + printf("%5d %02d %#B\n", seq_no, pcr, &hash); + count++; + } + e->destroy(e); + + printf("%d %N value%s found for component '%s'\n", count, + hash_algorithm_names, pts_meas_algo_to_hash(this->algo), + (count == 1) ? "" : "s", print_cfn(this->cfn)); + } + + } + else if (this->kid) + { + e = this->db->query(this->db, + "SELECT ch.seq_no, ch.pcr, ch.hash, " + "c.id, c.vendor_id, c.name, c.qualifier " + "FROM component_hashes AS ch " + "JOIN components AS c ON c.id = ch.component " + "WHERE ch.algo = ? AND ch.key = ? " + "ORDER BY vendor_id, name, qualifier, seq_no", + DB_INT, this->algo, DB_INT, this->kid, DB_INT, DB_INT, DB_BLOB, + DB_INT, DB_INT, DB_INT, DB_INT); + if (e) + { + while (e->enumerate(e, &seq_no, &pcr, &hash, &cid, &vid, &name, + &qualifier)) + { + if (cid != cid_old) + { + cfn = pts_comp_func_name_create(vid, name, qualifier); + printf("%3d: %s\n", cid, print_cfn(cfn)); + cfn->destroy(cfn); + cid_old = cid; + } + printf("%5d %02d %#B\n", seq_no, pcr, &hash); + count++; + } + e->destroy(e); + + printf("%d %N value%s found for key %#B '%s'\n", count, + hash_algorithm_names, pts_meas_algo_to_hash(this->algo), + (count == 1) ? "" : "s", &this->key, this->owner); + } + } +} + +METHOD(attest_db_t, add, bool, + private_attest_db_t *this) +{ + bool success = FALSE; + + if (this->kid && this->cid) + { + success = this->db->execute(this->db, NULL, + "INSERT INTO key_component (key, component) VALUES (?, ?)", + DB_UINT, this->kid, DB_UINT, this->cid) == 1; + + printf("key/component pair (%d/%d) %sinserted into database\n", + this->kid, this->cid, success ? "" : "could not be "); + } + return success; +} + +METHOD(attest_db_t, delete, bool, + private_attest_db_t *this) +{ + bool success; + + if (this->pid && (this->fid || this->did)) + { + printf("deletion of product/file entries not supported yet\n"); + return FALSE; + } + + if (this->kid && this->cid) + { + success = this->db->execute(this->db, NULL, + "DELETE FROM key_component " + "WHERE key = ? AND component = ?", + DB_UINT, this->kid, DB_UINT, this->cid) > 0; + + printf("key/component pair (%d/%d) %sdeleted from database\n", + this->kid, this->cid, success ? "" : "could not be "); + return success; + } + + if (this->cid) + { + success = this->db->execute(this->db, NULL, + "DELETE FROM components WHERE id = ?", + DB_UINT, this->cid) > 0; + + printf("component '%s' %sdeleted from database\n", print_cfn(this->cfn), + success ? "" : "could not be "); + return success; + } + + if (this->did) + { + success = this->db->execute(this->db, NULL, + "DELETE FROM files WHERE type = 1 AND id = ?", + DB_UINT, this->did) > 0; + + printf("directory '%s' %sdeleted from database\n", this->dir, + success ? "" : "could not be "); + return success; + } + + if (this->fid) + { + success = this->db->execute(this->db, NULL, + "DELETE FROM files WHERE id = ?", + DB_UINT, this->fid) > 0; + + printf("file '%s' %sdeleted from database\n", this->file, + success ? "" : "could not be "); + return success; + } + + if (this->kid) + { + success = this->db->execute(this->db, NULL, + "DELETE FROM keys WHERE id = ?", + DB_UINT, this->kid) > 0; + + printf("key %#B %sdeleted from database\n", &this->key, + success ? "" : "could not be "); + return success; + } + if (this->pid) + { + success = this->db->execute(this->db, NULL, + "DELETE FROM products WHERE id = ?", + DB_UINT, this->pid) > 0; + + printf("product '%s' %sdeleted from database\n", this->product, + success ? "" : "could not be "); + return success; + } + + printf("empty delete command\n"); + return FALSE; +} + +METHOD(attest_db_t, destroy, void, + private_attest_db_t *this) +{ + DESTROY_IF(this->db); + DESTROY_IF(this->cfn); + free(this->product); + free(this->file); + free(this->dir); + free(this->owner); + free(this->key.ptr); + free(this); +} + +/** + * Described in header. + */ +attest_db_t *attest_db_create(char *uri) +{ + private_attest_db_t *this; + + INIT(this, + .public = { + .set_component = _set_component, + .set_cid = _set_cid, + .set_directory = _set_directory, + .set_did = _set_did, + .set_file = _set_file, + .set_fid = _set_fid, + .set_key = _set_key, + .set_kid = _set_kid, + .set_product = _set_product, + .set_pid = _set_pid, + .set_algo = _set_algo, + .set_owner = _set_owner, + .list_products = _list_products, + .list_files = _list_files, + .list_components = _list_components, + .list_keys = _list_keys, + .list_hashes = _list_hashes, + .list_measurements = _list_measurements, + .add = _add, + .delete = _delete, + .destroy = _destroy, + }, + .dir = strdup(""), + .algo = PTS_MEAS_ALGO_SHA256, + .db = lib->db->create(lib->db, uri), + ); + + if (!this->db) + { + fprintf(stderr, "opening database failed.\n"); + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libpts/plugins/imv_attestation/attest_db.h b/src/libpts/plugins/imv_attestation/attest_db.h new file mode 100644 index 000000000..9c9a9dcba --- /dev/null +++ b/src/libpts/plugins/imv_attestation/attest_db.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2011 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 attest_db_t attest_db + * @{ @ingroup attest_db + */ + +#ifndef ATTEST_DB_H_ +#define ATTEST_DB_H_ + +#include <pts/pts_meas_algo.h> + +#include <library.h> + +typedef struct attest_db_t attest_db_t; + +/** + * Attestation database object + */ +struct attest_db_t { + + /** + * Set functional component to be queried + * + * @param comp functional component + * @param create if TRUE create database entry if it doesn't exist + * @return TRUE if successful + */ + bool (*set_component)(attest_db_t *this, char *comp, bool create); + + /** + * Set primary key of the functional component to be queried + * + * @param fid primary key of functional component + * @return TRUE if successful + */ + bool (*set_cid)(attest_db_t *this, int fid); + + /** + * Set directory to be queried + * + * @param dir directory + * @param create if TRUE create database entry if it doesn't exist + * @return TRUE if successful + */ + bool (*set_directory)(attest_db_t *this, char *dir, bool create); + + /** + * Set primary key of the directory to be queried + * + * @param did primary key of directory + * @return TRUE if successful + */ + bool (*set_did)(attest_db_t *this, int did); + + /** + * Set measurement file to be queried + * + * @param file measurement file + * @param create if TRUE create database entry if it doesn't exist + * @return TRUE if successful + */ + bool (*set_file)(attest_db_t *this, char *file, bool create); + + /** + * Set primary key of the measurement file to be queried + * + * @param fid primary key of measurement file + * @return TRUE if successful + */ + bool (*set_fid)(attest_db_t *this, int fid); + + /** + * Set functional component to be queried + * + * @param key AIK + * @param create if TRUE create database entry if it doesn't exist + * @return TRUE if successful + */ + bool (*set_key)(attest_db_t *this, chunk_t key, bool create); + + /** + * Set primary key of the AIK to be queried + * + * @param kid primary key of AIK + * @return TRUE if successful + */ + bool (*set_kid)(attest_db_t *this, int kid); + + /** + * Set software product to be queried + * + * @param product software product + * @param create if TRUE create database entry if it doesn't exist + * @return TRUE if successful + */ + bool (*set_product)(attest_db_t *this, char *product, bool create); + + /** + * Set primary key of the software product to be queried + * + * @param pid primary key of software product + * @return TRUE if successful + */ + bool (*set_pid)(attest_db_t *this, int pid); + + /** + * Set measurement hash algorithm + * + * @param algo hash algorithm + */ + void (*set_algo)(attest_db_t *this, pts_meas_algorithms_t algo); + + /** + * Set owner [user/host] of an AIK + * + * @param owner user/host name + * @return TRUE if successful + */ + void (*set_owner)(attest_db_t *this, char *owner); + + /** + * List all products stored in the database + */ + void (*list_products)(attest_db_t *this); + + /** + * List selected files stored in the database + */ + void (*list_files)(attest_db_t *this); + + /** + * List all components stored in the database + */ + void (*list_components)(attest_db_t *this); + + /** + * List all AIKs stored in the database + */ + void (*list_keys)(attest_db_t *this); + + /** + * List selected measurement hashes stored in the database + */ + void (*list_hashes)(attest_db_t *this); + + /** + * List selected component measurement stored in the database + */ + void (*list_measurements)(attest_db_t *this); + + /** + * Add an entry to the database + */ + bool (*add)(attest_db_t *this); + + /** + * Delete an entry from the database + */ + bool (*delete)(attest_db_t *this); + + /** + * Destroy attest_db_t object + */ + void (*destroy)(attest_db_t *this); + +}; + +/** + * Create an attest_db_t instance + * + * @param uri database URI + */ +attest_db_t* attest_db_create(char *uri); + +#endif /** ATTEST_DB_H_ @}*/ diff --git a/src/libpts/plugins/imv_attestation/attest_usage.c b/src/libpts/plugins/imv_attestation/attest_usage.c new file mode 100644 index 000000000..e58f821e0 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/attest_usage.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 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 <stdio.h> + +#include "attest_usage.h" + +/** + * print attest usage info + */ +void usage(void) +{ + printf("\ +Usage:\n\ + ipsec attest --files|--products|--keys|--hashes [options]\n\ + \n\ + ipsec attest --components|-keys|--measurements|--add|--del [options]\n\ + \n\ + ipsec attest --files [--product <name>|--pid <id>]\n\ + Show a list of files with a software product name or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --products [--file <path>|--fid <id>]\n\ + Show a list of supported software products with a file path or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --hashes [--sha1|--sha256|--sha384] [--product <name>|--pid <id>]\n\ + Show a list of measurement hashes for a given software product or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --hashes [--sha1|--sha256|--sha384] [--file <path>|--fid <id>]\n\ + Show a list of measurement hashes for a given file or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --components [--key <digest>|--kid <id>]\n\ + Show a list of components with an AIK digest or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --keys [--components <cfn>|--cid <id>]\n\ + Show a list of AIK key digests with a component or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --measurements [--sha1|--sha256|--sha384] [--component <cfn>|--cid <id>]\n\ + Show a list of component measurements for a given component or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --measurements [--sha1|--sha256|--sha384] [--key <digest>|--kid <id>|--aik <path>]\n\ + Show a list of component measurements for a given AIK or\n\ + its primary key as an optional selector.\n\ + \n\ + ipsec attest --add --file <path>|--dir <path>|--product <name>|--component <cfn>\n\ + Add a file, directory, product or component entry\n\ + Component <cfn> entries must be of the form <vendor_id>/<name>-<qualifier>\n\ + \n\ + ipsec attest --add [--owner <name>] --key <digest>|--aik <path>\n\ + Add an AIK public key digest entry preceded by an optional owner name\n\ + \n\ + ipsec attest --del --file <path>|--fid <id>|--dir <path>|--did <id>\n\ + Delete a file or directory entry referenced either by value or primary key\n\ + \n\ + ipsec attest --del --product <name>|--pid <id>|--component <cfn>|--cid <id>\n\ + Delete a product or component entry referenced either by value or primary key\n\ + \n\ + ipsec attest --del --key <digest>|--kid <id>|--aik <path>\n\ + Delete an AIK entry referenced either by value or primary key\n\ + \n"); +} + diff --git a/src/libpts/plugins/imv_attestation/attest_usage.h b/src/libpts/plugins/imv_attestation/attest_usage.h new file mode 100644 index 000000000..bce801e9d --- /dev/null +++ b/src/libpts/plugins/imv_attestation/attest_usage.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef ATTEST_USAGE_H_ +#define ATTEST_USAGE_H_ + +/** + * print attest usage info + */ +void usage(void); + + +#endif /* ATTEST_USAGE_H_ */ diff --git a/src/libimcv/plugins/imv_attestation/data.sql b/src/libpts/plugins/imv_attestation/data.sql index 60620dd45..e6e03627a 100644 --- a/src/libimcv/plugins/imv_attestation/data.sql +++ b/src/libpts/plugins/imv_attestation/data.sql @@ -36,272 +36,356 @@ INSERT INTO products ( 'Gentoo Base System release 1.12.11.1 i686' ); +INSERT INTO products ( + name +) VALUES ( + 'Ubuntu 11.10 i686' +); + /* Files */ -INSERT INTO files ( +INSERT INTO files ( /* 1 */ type, path - ) VALUES ( +) VALUES ( 0, '/lib/i386-linux-gnu/libdl.so.2' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, '/lib/x86_64-linux-gnu/libdl.so.2' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, '/lib/libdl.so.2' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, '/sbin/iptables' ); -INSERT INTO files ( +INSERT INTO files ( /* 5 */ type, path - ) VALUES ( +) VALUES ( 0, '/lib/libxtables.so.5' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, '/lib/libxtables.so.2' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 1, '/lib/xtables/' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libxt_udp.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libxt_tcp.so' ); -INSERT INTO files ( +INSERT INTO files ( /* 10 */ type, path - ) VALUES ( +) VALUES ( 0, 'libxt_esp.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libxt_policy.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libxt_conntrack.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libipt_SNAT.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libipt_DNAT.so' ); -INSERT INTO files ( +INSERT INTO files ( /* 15 */ type, path - ) VALUES ( +) VALUES ( 0, 'libipt_MASQUERADE.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libipt_LOG.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, '/sbin/ip6tables' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libip6t_LOG.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 0, 'libxt_mark.so' ); -INSERT INTO files ( +INSERT INTO files ( /* 20 */ type, path - ) VALUES ( +) VALUES ( 0, 'libxt_MARK.so' ); INSERT INTO files ( type, path - ) VALUES ( +) VALUES ( 1, '/lib/iptables' ); +INSERT INTO files ( + type, path +) VALUES ( + 0, '/etc/tnc_config' +); + /* Product-File */ INSERT INTO product_file ( - product, file + product, file, measurement +) VALUES ( + 1, 1, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 1, 4, 1 +); + +INSERT INTO product_file ( + product, file, measurement ) VALUES ( - 1, 1 + 1, 5, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 1, 4 + 1, 7, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 1, 5 + 1, 17, 1 ); INSERT INTO product_file ( - product, file + product, file, metadata ) VALUES ( - 1, 7 + 1, 22, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 1, 17 + 2, 2, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 2, 2 + 2, 4, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 2, 4 + 2, 5, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 2, 5 + 2, 7, 1 ); INSERT INTO product_file ( - product, file + product, file, metadata ) VALUES ( - 2, 7 + 2, 22, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 3, 3 + 3, 3, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 3, 4 + 3, 4, 1 ); INSERT INTO product_file ( - product, file + product, file, metadata ) VALUES ( - 4, 3 + 3, 22, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 4, 4 + 4, 3, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 4, 6 + 4, 4, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 4, 7 + 4, 6, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 5, 3 + 4, 7, 1 ); INSERT INTO product_file ( - product, file + product, file, metadata ) VALUES ( - 5, 4 + 4, 22, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 5, 6 + 5, 3, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 5, 7 + 5, 4, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 6, 3 + 5, 6, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 6, 4 + 5, 7, 1 ); INSERT INTO product_file ( - product, file + product, file, metadata ) VALUES ( - 6, 17 + 5, 22, 1 ); INSERT INTO product_file ( - product, file + product, file, measurement ) VALUES ( - 6, 21 + 6, 3, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 6, 4, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 6, 17, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 6, 21, 1 +); + +INSERT INTO product_file ( + product, file, metadata +) VALUES ( + 6, 22, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 7, 1, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 7, 4, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 7, 5, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 7, 7, 1 +); + +INSERT INTO product_file ( + product, file, measurement +) VALUES ( + 7, 17, 1 +); + +INSERT INTO product_file ( + product, file, metadata +) VALUES ( + 7, 22, 1 ); /* File Hashes */ @@ -327,6 +411,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, product, algo, hash ) VALUES ( + 1, 7, 32768, X'40763935cdea25119002c42f984b994d8d2a6d75' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 1, 7, 16384, X'27c4f867d3f994a361e0b25d7846b3698d29f82b38662f233a97cafc60c44189' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 1, 7, 8192, X'301dad8829308f5a68c603a87bf961b91365f0346ac2f322de3ddcbb4645f56c0e6d2dc503ec2abff8fe8e895ce9304d' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( 2, 2, 32768, X'2a4047437e6fb346e2d854fc415e16b80e75bf6b' ); @@ -489,6 +591,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, product, algo, hash ) VALUES ( + 4, 7, 32768, X'ff6deca0eeb7a257205c5f0ab5f5d821ea184098' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 4, 7, 16384, X'5c84fdf7c529d3c65a001587eda641fe489f83961a621fe514e7852a842690d6' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 4, 7, 8192, X'8bd699f85f5b3efb27204b4699c518f871ef245d03b4bf8d1cc00456025017546030c2f493525754cffcd24cdbc03b21' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( 5, 1, 32768, X'7a3ca72158e60b0c91e48a420848f1b693aea26c' ); @@ -525,6 +645,25 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, product, algo, hash ) VALUES ( + 5, 7, 32768, X'7a3ca72158e60b0c91e48a420848f1b693aea26c' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 5, 7, 16384, X'f9693c7d36c087d51f5012897fa0e8bb94081854d080c84f831f4d693d22f645' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 5, 7, 8192, X'4ec135e54c8840ab575fcdf00c66f996f763863ad30800b0f0a0b02e7899697d6ab9ccfe185ccbc16c19f38d0a27becb' +); + + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( 6, 4, 32768, X'92e66ae282947f66544682039a33fd1dbd402244' ); @@ -579,6 +718,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 8, 7, 7, 32768, X'11ce3b45feb3e66a75490d42ba95071ac6f40a7f' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 8, 7, 7, 16384, X'468ef70f19372bc4a2b1805ffa3621515061fc19fa361374788bd362d638ac02' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 8, 7, 7, 8192, X'63076ae505ce52c37878c9b6891ac516320046403aec25bf347c7011c2d28d5db7e2946d1fae3006ab4ef43716ff4558' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 8, 21, 6, 32768, X'010873de0d682a26e1c6795dd4992248cc47cdd1' ); @@ -633,6 +790,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 9, 7, 7, 32768, X'1d740abd38f9f4bc81ca434a0e25b6e21704248b' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 9, 7, 7, 16384, X'e26bb7175956dc8747a81431e810f830413b6c63756bf5156ab51367fe4f48a0' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 9, 7, 7, 8192, X'5d3637413b9e318d0e0be6a9da86121062b99d1bdb084dfda4222baa71b250de644b4024281760b4eae926e03fac4fdb' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 9, 21, 6, 32768, X'e1df4f3949b09c25e15b9c9b7088a60d683903a8' ); @@ -669,6 +844,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 10, 7, 7, 32768, X'339a58a1b313830c3cc74cb3fb52a5b8152f44e6' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 10, 7, 7, 16384, X'789f2c6a9382bb342964a12947ddf84735d3e3ed3aefbae407098738cdf7c686' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 10, 7, 7, 8192, X'858310a6e4b6311c491c4370990bfd6b9f03a49bb5ddf45b0d788f7043f130016e11be6bd95db66e49e2906a87adf8cb' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 10, 21, 6, 32768, X'87df2d01b85d8354819b431bae0a0a65bfc5d2db' ); @@ -705,6 +898,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 11, 7, 7, 32768, X'2d32ef93126abf8c660d57c67e5076c6394cabe8' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 11, 7, 7, 16384, X'ced29aca7fc2dd0b01d5d544dfb2e1640a6a79c657f589e7dd6636cfd63eda3b' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 11, 7, 7, 8192, X'a2d33fa2d0ee7bffa5e628f88ccb83cd61bb4c5fe6d2edb8b853b83d8c43f498fa6e8da70510f0a1a3ddb36060bbd4d8' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 12, 7, 1, 32768, X'6c0b2df4fc4c9122b5762ae140d53fdd1cf9e89b' ); @@ -723,6 +934,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 12, 7, 7, 32768, X'6c0b2df4fc4c9122b5762ae140d53fdd1cf9e89b' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 12, 7, 7, 16384, X'53c3f2bd5aaf8ef4c40f9af92a67621f5e67840b5ff2db67d1bccbcb56f7eef1' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 12, 7, 7, 8192, X'1a4a6d91bda3ce59e6c444ccc1e758c9c6f0e223fd8c5aac369260cdfa83081c0e8f3753f100490910ec161902f10ba7' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 13, 7, 1, 32768, X'e2f7b92abda769f82796f57a29801870585dcea3' ); @@ -741,6 +970,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 13, 7, 7, 32768, X'e2f7b92abda769f82796f57a29801870585dcea3' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 13, 7, 7, 16384, X'6d3fe67a040dbb469ef498b26cece45806cb7ca04787bba53b7ba1c18e2abd0a' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 13, 7, 7, 8192, X'014852b73cd3eabfa955b7bd56b269d5a0590a2770cf3d656b3d68dbad30884327fc81ff96c6f661c9c4189c3aefa346' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 14, 7, 1, 32768, X'160d2b04d11eb225fb148615b699081869e15b6c' ); @@ -759,6 +1006,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 14, 7, 7, 32768, X'160d2b04d11eb225fb148615b699081869e15b6c' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 14, 7, 7, 16384, X'1f5a2ceae1418f9c1fbf51eb7d84f74d488908cde5931a5461746d1e24682a25' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 14, 7, 7, 8192, X'f701cb25b0e9a9f32d3bba9b274ca0e8838363d13b7283b842d6c9673442890e538127c3b64ca4b177de1d243b44cf0d' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 15, 7, 1, 32768, X'5a0d07ab036603a76759e5f61f7d04f2d3c056cc' ); @@ -777,6 +1042,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 15, 7, 7, 32768, X'5a0d07ab036603a76759e5f61f7d04f2d3c056cc' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 15, 7, 7, 16384, X'85491714e860062c441ff50d93ad79350449596b89b2e409b513c2d883321c9d' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 15, 7, 7, 8192, X'8038830a994c779bc200e844d8768280feca9dd5d58de6cd359b87cc68846799edfd16e36e83002da4bb309cfd3b353d' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 16, 7, 1, 32768, X'd6c8dfbaae7ab28b5cef2626a2af3f99a6ea4365' ); @@ -793,6 +1076,24 @@ INSERT INTO file_hashes ( ); INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 16, 7, 7, 32768, X'd6c8dfbaae7ab28b5cef2626a2af3f99a6ea4365' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 16, 7, 7, 16384, X'd0d6f784e937227cce99e3be860be078d0397a6fb5a5bc9d95a19ef855609dbc' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 16, 7, 7, 8192, X'4be6e7978a6e4fb8a792815f2bbe28c2e66276401fb98ca90e49a5c2f2c94a1c7aac635d501d35d1db0fd53a0cb9d0fa' +); + +INSERT INTO file_hashes ( file, product, algo, hash ) VALUES ( 17, 1, 32768, X'8a7c41167bc0fcc1dec8329a868ba265c23857f5' @@ -829,6 +1130,24 @@ INSERT INTO file_hashes ( ); INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 17, 7, 32768, X'8a7c41167bc0fcc1dec8329a868ba265c23857f5' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 17, 7, 16384, X'f8eb857d7bb850f44c15363ba699442c2810663ac5a83a5f49e06e0fd8144b0e' +); + +INSERT INTO file_hashes ( + file, product, algo, hash +) VALUES ( + 17, 7, 8192, X'f40cb6e557ab18d70080e7995e3f96cc272842e822bf52bc1c59075313c2cd832f96cf03a8524905f3d3f7a61441c651' +); + +INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( 18, 7, 1, 32768, X'23296f48276e160b6d99b1b42a9114df720bb1ab' @@ -849,6 +1168,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 18, 7, 7, 32768, X'23296f48276e160b6d99b1b42a9114df720bb1ab' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 18, 7, 7, 16384, X'78cd0a598080e31453f477e8d8a12ec794e859f4076ed92e53d2053d6d16762c' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 18, 7, 7, 8192, X'4da3955f1fd968ecf95cff825d42715b544e577f28f411a020a270834235125bc0c8872bac8dd3466349ac8ab0aa2d74' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 19, 7, 1, 32768, X'd537d437f058136eb3d7be517dbe7647b623c619' ); @@ -867,6 +1204,24 @@ INSERT INTO file_hashes ( INSERT INTO file_hashes ( file, directory, product, algo, hash ) VALUES ( + 19, 7, 7, 32768, X'd537d437f058136eb3d7be517dbe7647b623c619' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 19, 7, 7, 16384, X'6a837037ad3fc4d06270d99cee2714dcf96b91aeb54d3483009219337961f834' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 19, 7, 7, 8192, X'7b5b16840da590a995fab23533f41982c5b136bff8e9b9a90b3c919a12cee20d312091455057a8bba9d9fbe314e6203d' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( 20, 7, 1, 32768, X'f9e3531abb67a020cf667d46ca823675dd0a0dd4' ); @@ -882,3 +1237,69 @@ INSERT INTO file_hashes ( 20, 7, 1, 8192, X'84200bd318bb022915150842ddf4002e061ef593604ad0d07021dc662cc40bfa749cce084ddf25d0e5137f6380f613d8' ); +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 20, 7, 7, 32768, X'f9e3531abb67a020cf667d46ca823675dd0a0dd4' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 20, 7, 7, 16384, X'569bafa2dabbcfa0ba9c7c411eacfeb8930f9d856a1a43cf8aa3662a67c13e35' +); + +INSERT INTO file_hashes ( + file, directory, product, algo, hash +) VALUES ( + 20, 7, 7, 8192, X'84200bd318bb022915150842ddf4002e061ef593604ad0d07021dc662cc40bfa749cce084ddf25d0e5137f6380f613d8' +); + +/* AIKs */ + +INSERT INTO keys ( + keyid, owner +) VALUES ( + X'b772a6730776b9f028e5adfccd40b55c320a13b6', 'Andreas, merthyr (Fujitsu Siemens Lifebook S6420)' +); + +/* Components */ + +INSERT INTO components ( + vendor_id, name, qualifier +) VALUES ( + 36906, 1, 33 /* ITA TGRUB */ +); + +INSERT INTO components ( + vendor_id, name, qualifier +) VALUES ( + 36906, 2, 33 /* ITA TBOOT */ +); + +INSERT INTO components ( + vendor_id, name, qualifier +) VALUES ( + 36906, 3, 33 /* ITA IMA */ +); + +/* AIK Component */ + +INSERT INTO key_component ( + key, component, depth, seq_no +) VALUES ( + 2, 2, 0, 1 +); + +INSERT INTO key_component ( + key, component, depth, seq_no +) VALUES ( + 1, 3, 0, 1 +); + +INSERT INTO key_component ( + key, component, depth, seq_no +) VALUES ( + 1, 2, 0, 2 +); + diff --git a/src/libpts/plugins/imv_attestation/imv_attestation.c b/src/libpts/plugins/imv_attestation/imv_attestation.c new file mode 100644 index 000000000..6bd5984e0 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/imv_attestation.c @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "imv_attestation_state.h" +#include "imv_attestation_process.h" +#include "imv_attestation_build.h" + +#include <imv/imv_agent.h> +#include <pa_tnc/pa_tnc_msg.h> +#include <ietf/ietf_attr.h> +#include <ietf/ietf_attr_pa_tnc_error.h> +#include <ietf/ietf_attr_product_info.h> + +#include <libpts.h> + +#include <pts/pts.h> +#include <pts/pts_database.h> +#include <pts/pts_creds.h> + +#include <tcg/tcg_attr.h> + +#include <tncif_pa_subtypes.h> + +#include <pen/pen.h> +#include <debug.h> +#include <credentials/credential_manager.h> +#include <utils/linked_list.h> + +/* IMV definitions */ + +static const char imv_name[] = "Attestation"; + +#define IMV_VENDOR_ID PEN_TCG +#define IMV_SUBTYPE PA_SUBTYPE_TCG_PTS + +static imv_agent_t *imv_attestation; + +/** + * Supported PTS measurement algorithms + */ +static pts_meas_algorithms_t supported_algorithms = PTS_MEAS_ALGO_NONE; + +/** + * Supported PTS Diffie Hellman Groups + */ +static pts_dh_group_t supported_dh_groups = PTS_DH_GROUP_NONE; + +/** + * PTS file measurement database + */ +static pts_database_t *pts_db; + +/** + * PTS credentials + */ +static pts_creds_t *pts_creds; + +/** + * PTS credential manager + */ +static credential_manager_t *pts_credmgr; + +/** + * see section 3.8.1 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_Initialize(TNC_IMVID imv_id, + TNC_Version min_version, + TNC_Version max_version, + TNC_Version *actual_version) +{ + char *hash_alg, *dh_group, *uri, *cadir; + + if (imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has already been initialized", imv_name); + return TNC_RESULT_ALREADY_INITIALIZED; + } + if (!pts_meas_algo_probe(&supported_algorithms) || + !pts_dh_group_probe(&supported_dh_groups)) + { + return TNC_RESULT_FATAL; + } + imv_attestation = imv_agent_create(imv_name, IMV_VENDOR_ID, IMV_SUBTYPE, + imv_id, actual_version); + if (!imv_attestation) + { + return TNC_RESULT_FATAL; + } + + libpts_init(); + + if (min_version > TNC_IFIMV_VERSION_1 || max_version < TNC_IFIMV_VERSION_1) + { + DBG1(DBG_IMV, "no common IF-IMV version"); + return TNC_RESULT_NO_COMMON_VERSION; + } + + hash_alg = lib->settings->get_str(lib->settings, + "libimcv.plugins.imv-attestation.hash_algorithm", "sha256"); + dh_group = lib->settings->get_str(lib->settings, + "libimcv.plugins.imv-attestation.dh_group", "ecp256"); + + if (!pts_meas_algo_update(hash_alg, &supported_algorithms) || + !pts_dh_group_update(dh_group, &supported_dh_groups)) + { + return TNC_RESULT_FATAL; + } + + /* create a PTS credential manager */ + pts_credmgr = credential_manager_create(); + + /* create PTS credential set */ + cadir = lib->settings->get_str(lib->settings, + "libimcv.plugins.imv-attestation.cadir", NULL); + pts_creds = pts_creds_create(cadir); + if (pts_creds) + { + pts_credmgr->add_set(pts_credmgr, pts_creds->get_set(pts_creds)); + } + + /* attach file measurement database */ + uri = lib->settings->get_str(lib->settings, + "libimcv.plugins.imv-attestation.database", NULL); + pts_db = pts_database_create(uri); + + return TNC_RESULT_SUCCESS; +} + +/** + * see section 3.8.2 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_NotifyConnectionChange(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_ConnectionState new_state) +{ + imv_state_t *state; + + if (!imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + switch (new_state) + { + case TNC_CONNECTION_STATE_CREATE: + state = imv_attestation_state_create(connection_id); + return imv_attestation->create_state(imv_attestation, state); + case TNC_CONNECTION_STATE_DELETE: + return imv_attestation->delete_state(imv_attestation, connection_id); + case TNC_CONNECTION_STATE_HANDSHAKE: + default: + return imv_attestation->change_state(imv_attestation, connection_id, + new_state, NULL); + } +} + +static TNC_Result send_message(TNC_ConnectionID connection_id) +{ + pa_tnc_msg_t *msg; + pa_tnc_attr_t *attr; + imv_state_t *state; + imv_attestation_state_t *attestation_state; + TNC_Result result; + linked_list_t *attr_list; + enumerator_t *enumerator; + + if (!imv_attestation->get_state(imv_attestation, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + attestation_state = (imv_attestation_state_t*)state; + attr_list = linked_list_create(); + + if (imv_attestation_build(attr_list, attestation_state, supported_algorithms, + supported_dh_groups, pts_db)) + { + if (attr_list->get_count(attr_list)) + { + msg = pa_tnc_msg_create(); + + /* move PA-TNC attributes to PA-TNC message */ + enumerator = attr_list->create_enumerator(attr_list); + while (enumerator->enumerate(enumerator, &attr)) + { + msg->add_attribute(msg, attr); + } + enumerator->destroy(enumerator); + + msg->build(msg); + result = imv_attestation->send_message(imv_attestation, + connection_id, FALSE, 0, TNC_IMCID_ANY, + msg->get_encoding(msg)); + msg->destroy(msg); + } + else + { + result = TNC_RESULT_SUCCESS; + } + attr_list->destroy(attr_list); + } + else + { + attr_list->destroy_offset(attr_list, offsetof(pa_tnc_attr_t, destroy)); + result = TNC_RESULT_FATAL; + } + + return result; +} + +static TNC_Result receive_message(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + chunk_t msg, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) +{ + pa_tnc_msg_t *pa_tnc_msg; + pa_tnc_attr_t *attr; + linked_list_t *attr_list; + imv_state_t *state; + imv_attestation_state_t *attestation_state; + pts_t *pts; + enumerator_t *enumerator; + TNC_Result result; + + if (!imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + + /* get current IMV state */ + if (!imv_attestation->get_state(imv_attestation, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + attestation_state = (imv_attestation_state_t*)state; + pts = attestation_state->get_pts(attestation_state); + + /* parse received PA-TNC message and automatically handle any errors */ + result = imv_attestation->receive_message(imv_attestation, state, msg, + msg_vid, msg_subtype, src_imc_id, dst_imv_id, &pa_tnc_msg); + + /* no parsed PA-TNC attributes available if an error occurred */ + if (!pa_tnc_msg) + { + return result; + } + + /* preprocess any IETF standard error attributes */ + result = pa_tnc_msg->process_ietf_std_errors(pa_tnc_msg) ? + TNC_RESULT_FATAL : TNC_RESULT_SUCCESS; + + attr_list = linked_list_create(); + + /* analyze PA-TNC attributes */ + enumerator = pa_tnc_msg->create_attribute_enumerator(pa_tnc_msg); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->get_vendor_id(attr) == PEN_IETF) + { + if (attr->get_type(attr) == IETF_ATTR_PA_TNC_ERROR) + { + ietf_attr_pa_tnc_error_t *error_attr; + pen_t error_vendor_id; + pa_tnc_error_code_t error_code; + chunk_t msg_info; + + error_attr = (ietf_attr_pa_tnc_error_t*)attr; + error_vendor_id = error_attr->get_vendor_id(error_attr); + + if (error_vendor_id == PEN_TCG) + { + error_code = error_attr->get_error_code(error_attr); + msg_info = error_attr->get_msg_info(error_attr); + + DBG1(DBG_IMV, "received TCG-PTS error '%N'", + pts_error_code_names, error_code); + DBG1(DBG_IMV, "error information: %B", &msg_info); + + result = TNC_RESULT_FATAL; + } + } + else if (attr->get_type(attr) == IETF_ATTR_PRODUCT_INFORMATION) + { + ietf_attr_product_info_t *attr_cast; + char *platform_info; + + attr_cast = (ietf_attr_product_info_t*)attr; + platform_info = attr_cast->get_info(attr_cast, NULL, NULL); + pts->set_platform_info(pts, platform_info); + } + } + else if (attr->get_vendor_id(attr) == PEN_TCG) + { + if (!imv_attestation_process(attr, attr_list, attestation_state, + supported_algorithms,supported_dh_groups, pts_db, pts_credmgr)) + { + result = TNC_RESULT_FATAL; + break; + } + } + } + enumerator->destroy(enumerator); + pa_tnc_msg->destroy(pa_tnc_msg); + + if (result != TNC_RESULT_SUCCESS) + { + attr_list->destroy_offset(attr_list, offsetof(pa_tnc_attr_t, destroy)); + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, + TNC_IMV_EVALUATION_RESULT_ERROR); + return imv_attestation->provide_recommendation(imv_attestation, + connection_id); + } + + if (attr_list->get_count(attr_list)) + { + pa_tnc_msg = pa_tnc_msg_create(); + + /* move PA-TNC attributes to PA-TNC message */ + enumerator = attr_list->create_enumerator(attr_list); + while (enumerator->enumerate(enumerator, &attr)) + { + pa_tnc_msg->add_attribute(pa_tnc_msg, attr); + } + enumerator->destroy(enumerator); + + pa_tnc_msg->build(pa_tnc_msg); + result = imv_attestation->send_message(imv_attestation, connection_id, + FALSE, 0, TNC_IMCID_ANY, + pa_tnc_msg->get_encoding(pa_tnc_msg)); + + pa_tnc_msg->destroy(pa_tnc_msg); + attr_list->destroy(attr_list); + + return result; + } + attr_list->destroy(attr_list); + + /* check the IMV state for the next PA-TNC attributes to send */ + result = send_message(connection_id); + if (result != TNC_RESULT_SUCCESS) + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, + TNC_IMV_EVALUATION_RESULT_ERROR); + return imv_attestation->provide_recommendation(imv_attestation, + connection_id); + } + + if (attestation_state->get_handshake_state(attestation_state) == + IMV_ATTESTATION_STATE_END) + { + if (attestation_state->get_file_meas_request_count(attestation_state)) + { + DBG1(DBG_IMV, "failure due to %d pending file measurements", + attestation_state->get_file_meas_request_count(attestation_state)); + attestation_state->set_measurement_error(attestation_state); + } + if (attestation_state->get_component_count(attestation_state)) + { + DBG1(DBG_IMV, "failure due to %d components waiting for evidence", + attestation_state->get_component_count(attestation_state)); + attestation_state->set_measurement_error(attestation_state); + } + if (attestation_state->get_measurement_error(attestation_state)) + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR); + } + else + { + state->set_recommendation(state, + TNC_IMV_ACTION_RECOMMENDATION_ALLOW, + TNC_IMV_EVALUATION_RESULT_COMPLIANT); + } + return imv_attestation->provide_recommendation(imv_attestation, + connection_id); + } + + return result; +} + +/** + * see section 3.8.4 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_MessageType msg_type) +{ + TNC_VendorID msg_vid; + TNC_MessageSubtype msg_subtype; + + msg_vid = msg_type >> 8; + msg_subtype = msg_type & TNC_SUBTYPE_ANY; + + return receive_message(imv_id, connection_id, 0, chunk_create(msg, msg_len), + msg_vid, msg_subtype, 0, TNC_IMVID_ANY); +} + +/** + * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ReceiveMessageLong(TNC_IMVID imv_id, + TNC_ConnectionID connection_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id) +{ + return receive_message(imv_id, connection_id, msg_flags, + chunk_create(msg, msg_len), msg_vid, msg_subtype, + src_imc_id, dst_imv_id); +} + +/** + * see section 3.8.7 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_SolicitRecommendation(TNC_IMVID imv_id, + TNC_ConnectionID connection_id) +{ + if (!imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + return imv_attestation->provide_recommendation(imv_attestation, + connection_id); +} + +/** + * see section 3.8.8 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_BatchEnding(TNC_IMVID imv_id, + TNC_ConnectionID connection_id) +{ + imv_state_t *state; + imv_attestation_state_t *attestation_state; + + if (!imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + /* get current IMV state */ + if (!imv_attestation->get_state(imv_attestation, connection_id, &state)) + { + return TNC_RESULT_FATAL; + } + attestation_state = (imv_attestation_state_t*)state; + + /* Check if IMV has to initiate the PA-TNC exchange */ + if (attestation_state->get_handshake_state(attestation_state) == + IMV_ATTESTATION_STATE_INIT) + { + return send_message(connection_id); + } + return TNC_RESULT_SUCCESS; +} + +/** + * see section 3.8.9 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) +{ + if (!imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + if (pts_creds) + { + pts_credmgr->remove_set(pts_credmgr, pts_creds->get_set(pts_creds)); + pts_creds->destroy(pts_creds); + } + DESTROY_IF(pts_db); + DESTROY_IF(pts_credmgr); + + libpts_deinit(); + + imv_attestation->destroy(imv_attestation); + imv_attestation = NULL; + + return TNC_RESULT_SUCCESS; +} + +/** + * see section 4.2.8.1 of TCG TNC IF-IMV Specification 1.3 + */ +TNC_Result TNC_IMV_ProvideBindFunction(TNC_IMVID imv_id, + TNC_TNCS_BindFunctionPointer bind_function) +{ + if (!imv_attestation) + { + DBG1(DBG_IMV, "IMV \"%s\" has not been initialized", imv_name); + return TNC_RESULT_NOT_INITIALIZED; + } + return imv_attestation->bind_functions(imv_attestation, bind_function); +} diff --git a/src/libpts/plugins/imv_attestation/imv_attestation_build.c b/src/libpts/plugins/imv_attestation/imv_attestation_build.c new file mode 100644 index 000000000..4f2cc1e95 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/imv_attestation_build.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "imv_attestation_build.h" +#include "imv_attestation_state.h" + +#include <libpts.h> +#include <tcg/tcg_pts_attr_proto_caps.h> +#include <tcg/tcg_pts_attr_meas_algo.h> +#include <tcg/tcg_pts_attr_dh_nonce_params_req.h> +#include <tcg/tcg_pts_attr_dh_nonce_finish.h> +#include <tcg/tcg_pts_attr_get_tpm_version_info.h> +#include <tcg/tcg_pts_attr_get_aik.h> +#include <tcg/tcg_pts_attr_req_func_comp_evid.h> +#include <tcg/tcg_pts_attr_gen_attest_evid.h> +#include <tcg/tcg_pts_attr_req_file_meas.h> +#include <tcg/tcg_pts_attr_req_file_meta.h> + +#include <debug.h> + +bool imv_attestation_build(linked_list_t *attr_list, + imv_attestation_state_t *attestation_state, + pts_meas_algorithms_t supported_algorithms, + pts_dh_group_t supported_dh_groups, + pts_database_t *pts_db) +{ + imv_attestation_handshake_state_t handshake_state; + pts_t *pts; + pa_tnc_attr_t *attr = NULL; + + handshake_state = attestation_state->get_handshake_state(attestation_state); + pts = attestation_state->get_pts(attestation_state); + + /** + * Skip DH Nonce Parameters Request attribute when + * DH Nonce Exchange is not selected by PTS-IMC side + */ + if (handshake_state == IMV_ATTESTATION_STATE_NONCE_REQ && + !(pts->get_proto_caps(pts) & PTS_PROTO_CAPS_D)) + { + DBG2(DBG_IMV, "PTS-IMC does not support DH Nonce negotiation - " + "advancing to TPM Initialization"); + handshake_state = IMV_ATTESTATION_STATE_TPM_INIT; + } + + /** + * Skip TPM Version Info and AIK attributes when + * no TPM is available on the PTS-IMC side + */ + if (handshake_state == IMV_ATTESTATION_STATE_TPM_INIT && + !(pts->get_proto_caps(pts) & PTS_PROTO_CAPS_T)) + { + DBG2(DBG_IMV, "PTS-IMC made no TPM available - " + "advancing to File Measurements"); + handshake_state = IMV_ATTESTATION_STATE_MEAS; + } + + switch (handshake_state) + { + case IMV_ATTESTATION_STATE_INIT: + { + pts_proto_caps_flag_t flags; + + /* Send Request Protocol Capabilities attribute */ + flags = pts->get_proto_caps(pts); + attr = tcg_pts_attr_proto_caps_create(flags, TRUE); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + /* Send Measurement Algorithms attribute */ + attr = tcg_pts_attr_meas_algo_create(supported_algorithms, FALSE); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_NONCE_REQ); + break; + } + case IMV_ATTESTATION_STATE_NONCE_REQ: + { + int min_nonce_len; + + /* Send DH nonce parameters request attribute */ + min_nonce_len = lib->settings->get_int(lib->settings, + "libimcv.plugins.imv-attestation.min_nonce_len", 0); + attr = tcg_pts_attr_dh_nonce_params_req_create(min_nonce_len, + supported_dh_groups); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_TPM_INIT); + break; + } + case IMV_ATTESTATION_STATE_TPM_INIT: + { + pts_meas_algorithms_t selected_algorithm; + chunk_t initiator_value, initiator_nonce; + + if ((pts->get_proto_caps(pts) & PTS_PROTO_CAPS_D)) + { + /* Send DH nonce finish attribute */ + selected_algorithm = pts->get_meas_algorithm(pts); + pts->get_my_public_value(pts, &initiator_value, &initiator_nonce); + attr = tcg_pts_attr_dh_nonce_finish_create(selected_algorithm, + initiator_value, initiator_nonce); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + } + + /* Send Get TPM Version attribute */ + attr = tcg_pts_attr_get_tpm_version_info_create(); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + /* Send Get AIK attribute */ + attr = tcg_pts_attr_get_aik_create(); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_MEAS); + break; + } + case IMV_ATTESTATION_STATE_MEAS: + { + enumerator_t *enumerator; + u_int32_t delimiter = SOLIDUS_UTF; + char *platform_info, *pathname; + u_int16_t request_id; + int id, type; + bool is_dir; + + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_COMP_EVID); + + /* Get Platform and OS of the PTS-IMC */ + platform_info = pts->get_platform_info(pts); + + if (!pts_db || !platform_info) + { + DBG1(DBG_IMV, "%s%s%s not available", + (pts_db) ? "" : "pts database", + (!pts_db && !platform_info) ? "and" : "", + (platform_info) ? "" : "platform info"); + break; + } + DBG1(DBG_IMV, "platform is '%s'", platform_info); + + /* Send Request File Metadata attribute */ + enumerator = pts_db->create_file_meta_enumerator(pts_db, + platform_info); + if (!enumerator) + { + break; + } + while (enumerator->enumerate(enumerator, &type, &pathname)) + { + is_dir = (type != 0); + DBG2(DBG_IMV, "metadata request for %s '%s'", + is_dir ? "directory" : "file", pathname); + attr = tcg_pts_attr_req_file_meta_create(is_dir, delimiter, + pathname); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + } + enumerator->destroy(enumerator); + + /* Send Request File Measurement attribute */ + enumerator = pts_db->create_file_meas_enumerator(pts_db, + platform_info); + if (!enumerator) + { + break; + } + while (enumerator->enumerate(enumerator, &id, &type, &pathname)) + { + is_dir = (type != 0); + request_id = attestation_state->add_file_meas_request( + attestation_state, id, is_dir); + DBG2(DBG_IMV, "measurement request %d for %s '%s'", + request_id, is_dir ? "directory" : "file", pathname); + attr = tcg_pts_attr_req_file_meas_create(is_dir, request_id, + delimiter, pathname); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + } + enumerator->destroy(enumerator); + break; + } + case IMV_ATTESTATION_STATE_COMP_EVID: + { + tcg_pts_attr_req_func_comp_evid_t *attr_cast; + enumerator_t *enumerator; + pts_component_t *comp; + pts_comp_func_name_t *comp_name; + chunk_t keyid; + int kid, vid, name, qualifier; + u_int8_t flags; + u_int32_t depth; + bool first = TRUE, first_component = TRUE; + + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_END); + + if (!(pts->get_proto_caps(pts) & PTS_PROTO_CAPS_T) || + !(pts->get_proto_caps(pts) & PTS_PROTO_CAPS_D)) + { + DBG2(DBG_IMV, "PTS-IMC made no TPM available - " + "skipping Component Measurements"); + break; + } + if (!pts->get_aik_keyid(pts, &keyid)) + { + DBG1(DBG_IMV, "retrieval of AIK keyid failed"); + return FALSE; + } + if (!pts_db) + { + DBG1(DBG_IMV, "pts database not available"); + break; + } + if (pts_db->check_aik_keyid(pts_db, keyid, &kid) != SUCCESS) + { + return FALSE; + } + enumerator = pts_db->create_comp_evid_enumerator(pts_db, kid); + if (!enumerator) + { + break; + } + while (enumerator->enumerate(enumerator, &vid, &name, + &qualifier, &depth)) + { + if (first) + { + DBG2(DBG_IMV, "evidence request by"); + first = FALSE; + } + comp_name = pts_comp_func_name_create(vid, name, qualifier); + comp_name->log(comp_name, " "); + + comp = pts_components->create(pts_components, comp_name, + depth, pts_db); + if (!comp) + { + DBG2(DBG_IMV, " not registered: removed from request"); + comp_name->destroy(comp_name); + continue; + } + attestation_state->add_component(attestation_state, comp); + if (first_component) + { + attr = tcg_pts_attr_req_func_comp_evid_create(); + attr->set_noskip_flag(attr, TRUE); + first_component = FALSE; + } + flags = comp->get_evidence_flags(comp); + /* TODO check flags against negotiated_caps */ + attr_cast = (tcg_pts_attr_req_func_comp_evid_t *)attr; + attr_cast->add_component(attr_cast, flags, depth, comp_name); + } + enumerator->destroy(enumerator); + + if (attr) + { + /* Send Request Functional Component Evidence attribute */ + attr_list->insert_last(attr_list, attr); + + /* Send Generate Attestation Evidence attribute */ + attr = tcg_pts_attr_gen_attest_evid_create(); + attr->set_noskip_flag(attr, TRUE); + attr_list->insert_last(attr_list, attr); + + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_EVID_FINAL); + } + break; + } + case IMV_ATTESTATION_STATE_EVID_FINAL: + attestation_state->set_handshake_state(attestation_state, + IMV_ATTESTATION_STATE_END); + break; + case IMV_ATTESTATION_STATE_END: + break; + } + return TRUE; +} diff --git a/src/libpts/plugins/imv_attestation/imv_attestation_build.h b/src/libpts/plugins/imv_attestation/imv_attestation_build.h new file mode 100644 index 000000000..7f934fd09 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/imv_attestation_build.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 imv_attestation_build_t imv_attestation_build + * @{ @ingroup imv_attestation_build + */ + +#ifndef IMV_ATTESTATION_BUILD_H_ +#define IMV_ATTESTATION_BUILD_H_ + +#include "imv_attestation_state.h" + +#include <pa_tnc/pa_tnc_msg.h> +#include <library.h> + +#include <pts/pts_database.h> +#include <pts/pts_dh_group.h> +#include <pts/pts_meas_algo.h> + +/** + * Process a TCG PTS attribute + * + * @param attr_list list of PA-TNC attriubutes to be built + * @param attestation_state attestation state of a given connection + * @param supported_algorithms supported PTS measurement algorithms + * @param supported_dh_groups supported DH groups + * @param pts_db PTS configuration database + * @return TRUE if successful + */ +bool imv_attestation_build(linked_list_t *attr_list, + imv_attestation_state_t *attestation_state, + pts_meas_algorithms_t supported_algorithms, + pts_dh_group_t supported_dh_groups, + pts_database_t *pts_db); + +#endif /** IMV_ATTESTATION_BUILD_H_ @}*/ diff --git a/src/libpts/plugins/imv_attestation/imv_attestation_process.c b/src/libpts/plugins/imv_attestation/imv_attestation_process.c new file mode 100644 index 000000000..a742b6697 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/imv_attestation_process.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "imv_attestation_process.h" + +#include <ietf/ietf_attr_pa_tnc_error.h> + +#include <pts/pts.h> + +#include <tcg/tcg_pts_attr_aik.h> +#include <tcg/tcg_pts_attr_dh_nonce_params_resp.h> +#include <tcg/tcg_pts_attr_file_meas.h> +#include <tcg/tcg_pts_attr_meas_algo.h> +#include <tcg/tcg_pts_attr_proto_caps.h> +#include <tcg/tcg_pts_attr_simple_comp_evid.h> +#include <tcg/tcg_pts_attr_simple_evid_final.h> +#include <tcg/tcg_pts_attr_tpm_version_info.h> +#include <tcg/tcg_pts_attr_unix_file_meta.h> + +#include <debug.h> +#include <crypto/hashers/hasher.h> + +#include <inttypes.h> + +bool imv_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, + imv_attestation_state_t *attestation_state, + pts_meas_algorithms_t supported_algorithms, + pts_dh_group_t supported_dh_groups, + pts_database_t *pts_db, + credential_manager_t *pts_credmgr) +{ + pts_t *pts; + + pts = attestation_state->get_pts(attestation_state); + + switch (attr->get_type(attr)) + { + case TCG_PTS_PROTO_CAPS: + { + tcg_pts_attr_proto_caps_t *attr_cast; + pts_proto_caps_flag_t flags; + + attr_cast = (tcg_pts_attr_proto_caps_t*)attr; + flags = attr_cast->get_flags(attr_cast); + pts->set_proto_caps(pts, flags); + break; + } + case TCG_PTS_MEAS_ALGO_SELECTION: + { + tcg_pts_attr_meas_algo_t *attr_cast; + pts_meas_algorithms_t selected_algorithm; + + attr_cast = (tcg_pts_attr_meas_algo_t*)attr; + selected_algorithm = attr_cast->get_algorithms(attr_cast); + if (!(selected_algorithm & supported_algorithms)) + { + DBG1(DBG_IMV, "PTS-IMC selected unsupported" + " measurement algorithm"); + return FALSE; + } + pts->set_meas_algorithm(pts, selected_algorithm); + break; + } + case TCG_PTS_DH_NONCE_PARAMS_RESP: + { + tcg_pts_attr_dh_nonce_params_resp_t *attr_cast; + int nonce_len, min_nonce_len; + pts_dh_group_t dh_group; + pts_meas_algorithms_t offered_algorithms, selected_algorithm; + chunk_t responder_value, responder_nonce; + + attr_cast = (tcg_pts_attr_dh_nonce_params_resp_t*)attr; + responder_nonce = attr_cast->get_responder_nonce(attr_cast); + + /* check compliance of responder nonce length */ + min_nonce_len = lib->settings->get_int(lib->settings, + "libimcv.plugins.imv-attestation.min_nonce_len", 0); + nonce_len = responder_nonce.len; + if (nonce_len < PTS_MIN_NONCE_LEN || + (min_nonce_len > 0 && nonce_len < min_nonce_len)) + { + attr = pts_dh_nonce_error_create( + max(PTS_MIN_NONCE_LEN, min_nonce_len), + PTS_MAX_NONCE_LEN); + attr_list->insert_last(attr_list, attr); + break; + } + + dh_group = attr_cast->get_dh_group(attr_cast); + if (!(dh_group & supported_dh_groups)) + { + DBG1(DBG_IMV, "PTS-IMC selected unsupported DH group"); + return FALSE; + } + + offered_algorithms = attr_cast->get_hash_algo_set(attr_cast); + selected_algorithm = pts_meas_algo_select(supported_algorithms, + offered_algorithms); + if (selected_algorithm == PTS_MEAS_ALGO_NONE) + { + attr = pts_hash_alg_error_create(supported_algorithms); + attr_list->insert_last(attr_list, attr); + break; + } + pts->set_dh_hash_algorithm(pts, selected_algorithm); + + if (!pts->create_dh_nonce(pts, dh_group, nonce_len)) + { + return FALSE; + } + + responder_value = attr_cast->get_responder_value(attr_cast); + pts->set_peer_public_value(pts, responder_value, + responder_nonce); + + /* Calculate secret assessment value */ + if (!pts->calculate_secret(pts)) + { + return FALSE; + } + break; + } + case TCG_PTS_TPM_VERSION_INFO: + { + tcg_pts_attr_tpm_version_info_t *attr_cast; + chunk_t tpm_version_info; + + attr_cast = (tcg_pts_attr_tpm_version_info_t*)attr; + tpm_version_info = attr_cast->get_tpm_version_info(attr_cast); + pts->set_tpm_version_info(pts, tpm_version_info); + break; + } + case TCG_PTS_AIK: + { + tcg_pts_attr_aik_t *attr_cast; + certificate_t *aik, *issuer; + public_key_t *public; + chunk_t keyid; + enumerator_t *e; + bool trusted = FALSE; + + attr_cast = (tcg_pts_attr_aik_t*)attr; + aik = attr_cast->get_aik(attr_cast); + if (!aik) + { + DBG1(DBG_IMV, "AIK unavailable"); + return FALSE; + } + if (aik->get_type(aik) == CERT_X509) + { + public = aik->get_public_key(aik); + public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &keyid); + DBG1(DBG_IMV, "verifying AIK certificate with keyid %#B", &keyid); + public->destroy(public); + + e = pts_credmgr->create_trusted_enumerator(pts_credmgr, + KEY_ANY, aik->get_issuer(aik), FALSE); + while (e->enumerate(e, &issuer)) + { + if (aik->issued_by(aik, issuer)) + { + trusted = TRUE; + break; + } + } + e->destroy(e); + DBG1(DBG_IMV, "AIK certificate is %strusted", + trusted ? "" : "not "); + if (!trusted) + { + return FALSE; + } + } + pts->set_aik(pts, aik); + break; + } + case TCG_PTS_FILE_MEAS: + { + tcg_pts_attr_file_meas_t *attr_cast; + u_int16_t request_id; + int file_count, file_id; + pts_meas_algorithms_t algo; + pts_file_meas_t *measurements; + char *platform_info; + enumerator_t *e_hash; + bool is_dir; + + platform_info = pts->get_platform_info(pts); + if (!pts_db || !platform_info) + { + DBG1(DBG_IMV, "%s%s%s not available", + (pts_db) ? "" : "pts database", + (!pts_db && !platform_info) ? "and" : "", + (platform_info) ? "" : "platform info"); + break; + } + + attr_cast = (tcg_pts_attr_file_meas_t*)attr; + measurements = attr_cast->get_measurements(attr_cast); + algo = pts->get_meas_algorithm(pts); + request_id = measurements->get_request_id(measurements); + file_count = measurements->get_file_count(measurements); + + DBG1(DBG_IMV, "measurement request %d returned %d file%s:", + request_id, file_count, (file_count == 1) ? "":"s"); + + if (!attestation_state->check_off_file_meas_request(attestation_state, + request_id, &file_id, &is_dir)) + { + DBG1(DBG_IMV, " no entry found for file measurement request %d", + request_id); + break; + } + + /* check hashes from database against measurements */ + e_hash = pts_db->create_file_hash_enumerator(pts_db, + platform_info, algo, file_id, is_dir); + if (!measurements->verify(measurements, e_hash, is_dir)) + { + attestation_state->set_measurement_error(attestation_state); + } + e_hash->destroy(e_hash); + break; + } + case TCG_PTS_UNIX_FILE_META: + { + tcg_pts_attr_file_meta_t *attr_cast; + int file_count; + pts_file_meta_t *metadata; + pts_file_metadata_t *entry; + time_t created, modified, accessed; + bool utc = FALSE; + enumerator_t *e; + + attr_cast = (tcg_pts_attr_file_meta_t*)attr; + metadata = attr_cast->get_metadata(attr_cast); + file_count = metadata->get_file_count(metadata); + + DBG1(DBG_IMV, "metadata request returned %d file%s:", + file_count, (file_count == 1) ? "":"s"); + + e = metadata->create_enumerator(metadata); + while (e->enumerate(e, &entry)) + { + DBG1(DBG_IMV, " '%s' (%"PRIu64" bytes)" + " owner %"PRIu64", group %"PRIu64", type %N", + entry->filename, entry->filesize, entry->owner, + entry->group, pts_file_type_names, entry->type); + + created = entry->created; + modified = entry->modified; + accessed = entry->accessed; + + DBG1(DBG_IMV, " created %T, modified %T, accessed %T", + &created, utc, &modified, utc, &accessed, utc); + } + e->destroy(e); + break; + } + case TCG_PTS_SIMPLE_COMP_EVID: + { + tcg_pts_attr_simple_comp_evid_t *attr_cast; + pts_comp_func_name_t *name; + pts_comp_evidence_t *evidence; + pts_component_t *comp; + u_int32_t depth; + status_t status; + + attr_cast = (tcg_pts_attr_simple_comp_evid_t*)attr; + evidence = attr_cast->get_comp_evidence(attr_cast); + name = evidence->get_comp_func_name(evidence, &depth); + + comp = attestation_state->check_off_component(attestation_state, name); + if (!comp) + { + DBG1(DBG_IMV, " no entry found for component evidence request"); + break; + } + status = comp->verify(comp, pts, evidence); + + switch (status) + { + default: + case FAILED: + attestation_state->set_measurement_error(attestation_state); + comp->destroy(comp); + break; + case SUCCESS: + name->log(name, " successfully measured "); + comp->destroy(comp); + break; + case NEED_MORE: + /* re-enter component into list */ + attestation_state->add_component(attestation_state, comp); + } + break; + } + case TCG_PTS_SIMPLE_EVID_FINAL: + { + tcg_pts_attr_simple_evid_final_t *attr_cast; + u_int8_t flags; + pts_meas_algorithms_t comp_hash_algorithm; + chunk_t pcr_comp, tpm_quote_sig, evid_sig; + chunk_t pcr_composite, quote_info; + bool use_quote2, use_ver_info; + + attr_cast = (tcg_pts_attr_simple_evid_final_t*)attr; + flags = attr_cast->get_quote_info(attr_cast, &comp_hash_algorithm, + &pcr_comp, &tpm_quote_sig); + + if (flags != PTS_SIMPLE_EVID_FINAL_NO) + { + use_quote2 = (flags == PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 || + flags == PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER); + use_ver_info = (flags == PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER); + + /* Construct PCR Composite and TPM Quote Info structures */ + if (!pts->get_quote_info(pts, use_quote2, use_ver_info, + comp_hash_algorithm, &pcr_composite, "e_info)) + { + DBG1(DBG_IMV, "unable to construct TPM Quote Info"); + return FALSE; + } + + if (!chunk_equals(pcr_comp, pcr_composite)) + { + DBG1(DBG_IMV, "received PCR Composite does not match " + "constructed one"); + free(pcr_composite.ptr); + free(quote_info.ptr); + return FALSE; + } + DBG2(DBG_IMV, "received PCR Composite matches constructed one"); + free(pcr_composite.ptr); + + if (!pts->verify_quote_signature(pts, quote_info, tpm_quote_sig)) + { + free(quote_info.ptr); + return FALSE; + } + DBG2(DBG_IMV, "TPM Quote Info signature verification successful"); + free(quote_info.ptr); + + /* Finalize any pending measurement registrations */ + attestation_state->check_off_registrations(attestation_state); + } + + if (attr_cast->get_evid_sig(attr_cast, &evid_sig)) + { + /** TODO: What to do with Evidence Signature */ + DBG1(DBG_IMV, "this version of the Attestation IMV can not " + "handle Evidence Signatures"); + } + break; + } + + /* TODO: Not implemented yet */ + case TCG_PTS_INTEG_MEAS_LOG: + /* Attributes using XML */ + case TCG_PTS_TEMPL_REF_MANI_SET_META: + case TCG_PTS_VERIFICATION_RESULT: + case TCG_PTS_INTEG_REPORT: + /* On Windows only*/ + case TCG_PTS_WIN_FILE_META: + case TCG_PTS_REGISTRY_VALUE: + /* Received on IMC side only*/ + case TCG_PTS_REQ_PROTO_CAPS: + case TCG_PTS_DH_NONCE_PARAMS_REQ: + case TCG_PTS_DH_NONCE_FINISH: + case TCG_PTS_MEAS_ALGO: + case TCG_PTS_GET_TPM_VERSION_INFO: + case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META: + case TCG_PTS_UPDATE_TEMPL_REF_MANI: + case TCG_PTS_GET_AIK: + case TCG_PTS_REQ_FUNC_COMP_EVID: + case TCG_PTS_GEN_ATTEST_EVID: + case TCG_PTS_REQ_FILE_META: + case TCG_PTS_REQ_FILE_MEAS: + case TCG_PTS_REQ_INTEG_MEAS_LOG: + default: + DBG1(DBG_IMV, "received unsupported attribute '%N'", + tcg_attr_names, attr->get_type(attr)); + break; + } + return TRUE; +} + diff --git a/src/libpts/plugins/imv_attestation/imv_attestation_process.h b/src/libpts/plugins/imv_attestation/imv_attestation_process.h new file mode 100644 index 000000000..4d4eeefbb --- /dev/null +++ b/src/libpts/plugins/imv_attestation/imv_attestation_process.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 imv_attestation_process_t imv_attestation_process + * @{ @ingroup imv_attestation_process + */ + +#ifndef IMV_ATTESTATION_PROCESS_H_ +#define IMV_ATTESTATION_PROCESS_H_ + +#include "imv_attestation_state.h" + +#include <library.h> +#include <utils/linked_list.h> +#include <credentials/credential_manager.h> +#include <crypto/hashers/hasher.h> + +#include <pa_tnc/pa_tnc_attr.h> + +#include <pts/pts_database.h> +#include <pts/pts_dh_group.h> +#include <pts/pts_meas_algo.h> + +/** + * Process a TCG PTS attribute + * + * @param attr PA-TNC attribute to be processed + * @param attr_list list with PA-TNC error attributes + * @param attestation_state attestation state of a given connection + * @param supported_algorithms supported PTS measurement algorithms + * @param supported_dh_groups supported DH groups + * @param pts_db PTS configuration database + * @param pts_credmgr PTS credential manager + * @return TRUE if successful + */ +bool imv_attestation_process(pa_tnc_attr_t *attr, linked_list_t *attr_list, + imv_attestation_state_t *attestation_state, + pts_meas_algorithms_t supported_algorithms, + pts_dh_group_t supported_dh_groups, + pts_database_t *pts_db, + credential_manager_t *pts_credmgr); + +#endif /** IMV_ATTESTATION_PROCESS_H_ @}*/ diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_state.c b/src/libpts/plugins/imv_attestation/imv_attestation_state.c index 6305dac2f..a58fd3ec3 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_state.c +++ b/src/libpts/plugins/imv_attestation/imv_attestation_state.c @@ -20,12 +20,12 @@ #include <debug.h> typedef struct private_imv_attestation_state_t private_imv_attestation_state_t; -typedef struct request_t request_t; +typedef struct file_meas_request_t file_meas_request_t; /** * PTS File/Directory Measurement request entry */ -struct request_t { +struct file_meas_request_t { u_int16_t id; int file_id; bool is_dir; @@ -52,6 +52,16 @@ struct private_imv_attestation_state_t { TNC_ConnectionState state; /** + * Does the TNCCS connection support long message types? + */ + bool has_long; + + /** + * Does the TNCCS connection support exclusive delivery? + */ + bool has_excl; + + /** * IMV Attestation handshake state */ imv_attestation_handshake_state_t handshake_state; @@ -67,20 +77,30 @@ struct private_imv_attestation_state_t { TNC_IMV_Evaluation_Result eval; /** - * Request counter + * File Measurement Request counter */ - u_int16_t request_counter; + u_int16_t file_meas_request_counter; /** * List of PTS File/Directory Measurement requests */ - linked_list_t *requests; + linked_list_t *file_meas_requests; + + /** + * List of Functional Components + */ + linked_list_t *components; /** * PTS object */ pts_t *pts; + /** + * Measurement error + */ + bool measurement_error; + }; typedef struct entry_t entry_t; @@ -97,9 +117,12 @@ struct entry_t { * Table of multi-lingual reason string entries */ static entry_t reasons[] = { - { "en", "IMC Attestation Measurement/s of requested file didn't match" }, - { "mn", "IMC Attestation Шалгахаар тохируулсан файлуудын хэмжилтүүд таарсангүй" }, - { "de", "IMC Attestation Messung/en von angefordeten Datein stimmt nicht überein" }, + { "en", "IMV Attestation: Incorrect/pending file measurement/component" + " evidence or invalid TPM Quote signature received" }, + { "mn", "IMV Attestation: Буруу/хүлээгдэж байгаа файл/компонент хэмжилт " + "эсвэл буруу TPM Quote гарын үсэг" }, + { "de", "IMV Attestation: Falsche/Fehlende Dateimessung/Komponenten Beweis " + "oder ungültige TPM Quote Unterschrift ist erhalten" }, }; METHOD(imv_state_t, get_connection_id, TNC_ConnectionID, @@ -108,6 +131,25 @@ METHOD(imv_state_t, get_connection_id, TNC_ConnectionID, return this->connection_id; } +METHOD(imv_state_t, has_long, bool, + private_imv_attestation_state_t *this) +{ + return this->has_long; +} + +METHOD(imv_state_t, has_excl, bool, + private_imv_attestation_state_t *this) +{ + return this->has_excl; +} + +METHOD(imv_state_t, set_flags, void, + private_imv_attestation_state_t *this, bool has_long, bool has_excl) +{ + this->has_long = has_long; + this->has_excl = has_excl; +} + METHOD(imv_state_t, change_state, void, private_imv_attestation_state_t *this, TNC_ConnectionState new_state) { @@ -177,19 +219,22 @@ METHOD(imv_state_t, get_reason_string, bool, METHOD(imv_state_t, destroy, void, private_imv_attestation_state_t *this) { - this->requests->destroy_function(this->requests, free); + this->file_meas_requests->destroy_function(this->file_meas_requests, free); + this->components->destroy_offset(this->components, + offsetof(pts_component_t, destroy)); this->pts->destroy(this->pts); free(this); } -METHOD(imv_attestation_state_t, get_handshake_state, imv_attestation_handshake_state_t, - private_imv_attestation_state_t *this) +METHOD(imv_attestation_state_t, get_handshake_state, + imv_attestation_handshake_state_t, private_imv_attestation_state_t *this) { return this->handshake_state; } METHOD(imv_attestation_state_t, set_handshake_state, void, - private_imv_attestation_state_t *this, imv_attestation_handshake_state_t new_state) + private_imv_attestation_state_t *this, + imv_attestation_handshake_state_t new_state) { this->handshake_state = new_state; } @@ -200,29 +245,29 @@ METHOD(imv_attestation_state_t, get_pts, pts_t*, return this->pts; } -METHOD(imv_attestation_state_t, add_request, u_int16_t, +METHOD(imv_attestation_state_t, add_file_meas_request, u_int16_t, private_imv_attestation_state_t *this, int file_id, bool is_dir) { - request_t *request; + file_meas_request_t *request; - request = malloc_thing(request_t); - request->id = ++this->request_counter; + request = malloc_thing(file_meas_request_t); + request->id = ++this->file_meas_request_counter; request->file_id = file_id; request->is_dir = is_dir; - this->requests->insert_last(this->requests, request); + this->file_meas_requests->insert_last(this->file_meas_requests, request); - return this->request_counter; + return this->file_meas_request_counter; } -METHOD(imv_attestation_state_t, check_off_request, bool, +METHOD(imv_attestation_state_t, check_off_file_meas_request, bool, private_imv_attestation_state_t *this, u_int16_t id, int *file_id, bool* is_dir) { enumerator_t *enumerator; - request_t *request; + file_meas_request_t *request; bool found = FALSE; - enumerator = this->requests->create_enumerator(this->requests); + enumerator = this->file_meas_requests->create_enumerator(this->file_meas_requests); while (enumerator->enumerate(enumerator, &request)) { if (request->id == id) @@ -230,7 +275,7 @@ METHOD(imv_attestation_state_t, check_off_request, bool, found = TRUE; *file_id = request->file_id; *is_dir = request->is_dir; - this->requests->remove_at(this->requests, enumerator); + this->file_meas_requests->remove_at(this->file_meas_requests, enumerator); free(request); break; } @@ -239,10 +284,72 @@ METHOD(imv_attestation_state_t, check_off_request, bool, return found; } -METHOD(imv_attestation_state_t, get_request_count, int, +METHOD(imv_attestation_state_t, get_file_meas_request_count, int, + private_imv_attestation_state_t *this) +{ + return this->file_meas_requests->get_count(this->file_meas_requests); +} + +METHOD(imv_attestation_state_t, add_component, void, + private_imv_attestation_state_t *this, pts_component_t *entry) +{ + this->components->insert_last(this->components, entry); +} + +METHOD(imv_attestation_state_t, check_off_component, pts_component_t*, + private_imv_attestation_state_t *this, pts_comp_func_name_t *name) +{ + enumerator_t *enumerator; + pts_component_t *entry, *found = NULL; + + enumerator = this->components->create_enumerator(this->components); + while (enumerator->enumerate(enumerator, &entry)) + { + if (name->equals(name, entry->get_comp_func_name(entry))) + { + found = entry; + this->components->remove_at(this->components, enumerator); + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +METHOD(imv_attestation_state_t, check_off_registrations, void, + private_imv_attestation_state_t *this) +{ + enumerator_t *enumerator; + pts_component_t *entry; + + enumerator = this->components->create_enumerator(this->components); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->check_off_registrations(entry)) + { + this->components->remove_at(this->components, enumerator); + entry->destroy(entry); + } + } + enumerator->destroy(enumerator); +} + +METHOD(imv_attestation_state_t, get_component_count, int, + private_imv_attestation_state_t *this) +{ + return this->components->get_count(this->components); +} + +METHOD(imv_attestation_state_t, get_measurement_error, bool, + private_imv_attestation_state_t *this) +{ + return this->measurement_error; +} + +METHOD(imv_attestation_state_t, set_measurement_error, void, private_imv_attestation_state_t *this) { - return this->requests->get_count(this->requests); + this->measurement_error = TRUE; } /** @@ -257,6 +364,9 @@ imv_state_t *imv_attestation_state_create(TNC_ConnectionID connection_id) .public = { .interface = { .get_connection_id = _get_connection_id, + .has_long = _has_long, + .has_excl = _has_excl, + .set_flags = _set_flags, .change_state = _change_state, .get_recommendation = _get_recommendation, .set_recommendation = _set_recommendation, @@ -266,16 +376,23 @@ imv_state_t *imv_attestation_state_create(TNC_ConnectionID connection_id) .get_handshake_state = _get_handshake_state, .set_handshake_state = _set_handshake_state, .get_pts = _get_pts, - .add_request = _add_request, - .check_off_request = _check_off_request, - .get_request_count = _get_request_count, + .add_file_meas_request = _add_file_meas_request, + .check_off_file_meas_request = _check_off_file_meas_request, + .get_file_meas_request_count = _get_file_meas_request_count, + .add_component = _add_component, + .check_off_component = _check_off_component, + .check_off_registrations = _check_off_registrations, + .get_component_count = _get_component_count, + .get_measurement_error = _get_measurement_error, + .set_measurement_error = _set_measurement_error, }, .connection_id = connection_id, .state = TNC_CONNECTION_STATE_CREATE, .handshake_state = IMV_ATTESTATION_STATE_INIT, .rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, .eval = TNC_IMV_EVALUATION_RESULT_DONT_KNOW, - .requests = linked_list_create(), + .file_meas_requests = linked_list_create(), + .components = linked_list_create(), .pts = pts_create(FALSE), ); diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_state.h b/src/libpts/plugins/imv_attestation/imv_attestation_state.h index a79fc91cc..0e2c04da4 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_state.h +++ b/src/libpts/plugins/imv_attestation/imv_attestation_state.h @@ -24,6 +24,7 @@ #include <imv/imv_state.h> #include <pts/pts.h> +#include <pts/components/pts_component.h> #include <library.h> typedef struct imv_attestation_state_t imv_attestation_state_t; @@ -34,9 +35,11 @@ typedef enum imv_attestation_handshake_state_t imv_attestation_handshake_state_t */ enum imv_attestation_handshake_state_t { IMV_ATTESTATION_STATE_INIT, + IMV_ATTESTATION_STATE_NONCE_REQ, + IMV_ATTESTATION_STATE_TPM_INIT, IMV_ATTESTATION_STATE_MEAS, IMV_ATTESTATION_STATE_COMP_EVID, - IMV_ATTESTATION_STATE_IML, + IMV_ATTESTATION_STATE_EVID_FINAL, IMV_ATTESTATION_STATE_END, }; @@ -55,7 +58,8 @@ struct imv_attestation_state_t { * * @return the handshake state of IMV */ - imv_attestation_handshake_state_t (*get_handshake_state)(imv_attestation_state_t *this); + imv_attestation_handshake_state_t (*get_handshake_state)( + imv_attestation_state_t *this); /** * Set state of the handshake @@ -79,15 +83,15 @@ struct imv_attestation_state_t { * @param is_dir TRUE if directory * @return unique request ID */ - u_int16_t (*add_request)(imv_attestation_state_t *this, int file_id, - bool is_dir); + u_int16_t (*add_file_meas_request)(imv_attestation_state_t *this, + int file_id, bool is_dir); /** * Returns the number of pending file/directory measurement requests * * @return number of pending requests */ - int (*get_request_count)(imv_attestation_state_t *this); + int (*get_file_meas_request_count)(imv_attestation_state_t *this); /** * Check for presence of request_id and if found remove it from the list @@ -97,8 +101,48 @@ struct imv_attestation_state_t { * @param is_dir return TRUE if request was for a directory * @return TRUE if request ID found, FALSE otherwise */ - bool (*check_off_request)(imv_attestation_state_t *this, u_int16_t id, - int *file_id, bool *is_dir); + bool (*check_off_file_meas_request)(imv_attestation_state_t *this, + u_int16_t id, int *file_id, bool *is_dir); + + /** + * Add an entry to the list of Functional Components waiting for evidence + * + * @param entry Functional Component + */ + void (*add_component)(imv_attestation_state_t *this, pts_component_t *entry); + + /** + * Returns the number of Functional Component waiting for evidence + * + * @return Number of waiting Functional Components + */ + int (*get_component_count)(imv_attestation_state_t *this); + + /** + * Check for presence of Functional Component and remove and return it + * + * @param name Name of the requested Functional Component + * @return Functional Component if found, NULL otherwise + */ + pts_component_t* (*check_off_component)(imv_attestation_state_t *this, + pts_comp_func_name_t *name); + + /** + * Tell the Functional Components to finalize any measurement registrations + */ + void (*check_off_registrations)(imv_attestation_state_t *this); + + /** + * Indicates if a file measurement error occurred + * + * @return TRUE in case of measurement error + */ + bool (*get_measurement_error)(imv_attestation_state_t *this); + + /** + * Call if a file measurement error is encountered + */ + void (*set_measurement_error)(imv_attestation_state_t *this); }; diff --git a/src/libpts/plugins/imv_attestation/tables.sql b/src/libpts/plugins/imv_attestation/tables.sql new file mode 100644 index 000000000..703557a07 --- /dev/null +++ b/src/libpts/plugins/imv_attestation/tables.sql @@ -0,0 +1,82 @@ +/* PTS SQLite database */ + +DROP TABLE IF EXISTS files; +CREATE TABLE files ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + type INTEGER NOT NULL, + path TEXT NOT NULL +); + +DROP TABLE IF EXISTS products; +CREATE TABLE products ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL +); +DROP INDEX IF EXISTS products_name; +CREATE INDEX products_name ON products ( + name +); + +DROP TABLE IF EXISTS product_file; +CREATE TABLE product_file ( + product INTEGER NOT NULL, + file INTEGER NOT NULL, + measurement INTEGER DEFAULT 0, + metadata INTEGER DEFAULT 0, + PRIMARY KEY (product, file) +); + +DROP TABLE IF EXISTS file_hashes; +CREATE TABLE file_hashes ( + file INTEGER NOT NULL, + directory INTEGER DEFAULT 0, + product INTEGER NOT NULL, + algo INTEGER NOT NULL, + hash BLOB NOT NULL, + PRIMARY KEY(file, directory, product, algo) +); + +DROP TABLE IF EXISTS keys; +CREATE TABLE keys ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + keyid BLOB NOT NULL, + owner TEXT NOT NULL +); +DROP INDEX IF EXISTS keys_keyid; +CREATE INDEX keys_keyid ON keys ( + keyid +); +DROP INDEX IF EXISTS keys_owner; +CREATE INDEX keys_owner ON keys ( + owner +); + +DROP TABLE IF EXISTS components; +CREATE TABLE components ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + vendor_id INTEGER NOT NULL, + name INTEGER NOT NULL, + qualifier INTEGER DEFAULT 0 +); + + +DROP TABLE IF EXISTS key_component; +CREATE TABLE key_component ( + key INTEGER NOT NULL, + component INTEGER NOT NULL, + depth INTEGER DEFAULT 0, + seq_no INTEGER DEFAULT 0, + PRIMARY KEY (key, component) +); + + +DROP TABLE IF EXISTS component_hashes; +CREATE TABLE component_hashes ( + component INTEGER NOT NULL, + key INTEGER NOT NULL, + seq_no INTEGER NOT NULL, + pcr INTEGER NOT NULL, + algo INTEGER NOT NULL, + hash BLOB NOT NULL, + PRIMARY KEY(component, key, seq_no, algo) +); diff --git a/src/libpts/pts/components/ita/ita_comp_func_name.c b/src/libpts/pts/components/ita/ita_comp_func_name.c new file mode 100644 index 000000000..a593281ba --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_func_name.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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 "ita_comp_func_name.h" + +char pts_ita_qualifier_flag_names[] = { 'K', 'S' }; + +ENUM_BEGIN(pts_ita_qualifier_type_names, PTS_ITA_QUALIFIER_TYPE_UNKNOWN, + PTS_ITA_QUALIFIER_TYPE_TNC, + "Unknown", + "Trusted Platform", + "Operating System", + "Graphical User Interface", + "Application", + "Networking", + "Library", + "TNC Defined Component" +); +ENUM_NEXT(pts_ita_qualifier_type_names, PTS_ITA_QUALIFIER_TYPE_ALL, + PTS_ITA_QUALIFIER_TYPE_ALL, + PTS_ITA_QUALIFIER_TYPE_TNC, + "All Matching Components" +); +ENUM_END(pts_ita_qualifier_type_names, PTS_ITA_QUALIFIER_TYPE_ALL); + +ENUM(pts_ita_comp_func_names, PTS_ITA_COMP_FUNC_NAME_IGNORE, + PTS_ITA_COMP_FUNC_NAME_IMA, + "Ignore", + "Trusted GRUB Boot Loader", + "Trusted Boot", + "Linux IMA" +); + diff --git a/src/libpts/pts/components/ita/ita_comp_func_name.h b/src/libpts/pts/components/ita/ita_comp_func_name.h new file mode 100644 index 000000000..eb2f363f3 --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_func_name.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_ita_comp_func_name pts_ita_comp_func_name + * @{ @ingroup pts + */ + +#ifndef PTS_ITA_COMP_FUNC_NAME_H_ +#define PTS_ITA_COMP_FUNC_NAME_H_ + +typedef enum pts_ita_qualifier_type_t pts_ita_qualifier_type_t; +typedef enum pts_ita_comp_func_name_t pts_ita_comp_func_name_t; + +#include <library.h> + +/** + * PTS Component Functional Name Qualifier Flags for the ITA namespace + */ +#define PTS_ITA_QUALIFIER_FLAG_KERNEL (1<<5) +#define PTS_ITA_QUALIFIER_FLAG_SUB (1<<4) + +extern char pts_ita_qualifier_flag_names[]; + +/** + * Size of the PTS Component Functional Name Qualifier Type field + */ +#define PTS_ITA_QUALIFIER_TYPE_SIZE 4 + +/** + * PTS Component Functional Name Qualifier Types for the ITA namespace + * equal to section 5.2 of PTS Protocol: Binding to TNC IF-M Specification + */ +enum pts_ita_qualifier_type_t { + /** Unknown */ + PTS_ITA_QUALIFIER_TYPE_UNKNOWN = 0x0, + /** Trusted Platform */ + PTS_ITA_QUALIFIER_TYPE_TRUSTED = 0x1, + /** Operating System */ + PTS_ITA_QUALIFIER_TYPE_OS = 0x2, + /** Graphical User Interface */ + PTS_ITA_QUALIFIER_TYPE_GUI = 0x3, + /** Application */ + PTS_ITA_QUALIFIER_TYPE_APP = 0x4, + /** Networking */ + PTS_ITA_QUALIFIER_TYPE_NET = 0x5, + /** Library */ + PTS_ITA_QUALIFIER_TYPE_LIB = 0x6, + /** TNC Defined Component */ + PTS_ITA_QUALIFIER_TYPE_TNC = 0x7, + /** All Matching Components */ + PTS_ITA_QUALIFIER_TYPE_ALL = 0xF, +}; + +extern enum_name_t *pts_ita_qualifier_type_names; + +/** + * PTS Component Functional Name Binary Enumeration for the ITA namespace + */ +enum pts_ita_comp_func_name_t { + /** Ignore */ + PTS_ITA_COMP_FUNC_NAME_IGNORE = 0x0000, + /** Trusted GRUB Boot Loader */ + PTS_ITA_COMP_FUNC_NAME_TGRUB = 0x0001, + /** Trusted Boot */ + PTS_ITA_COMP_FUNC_NAME_TBOOT = 0x0002, + /** Linux Integrity Measurement Architecture */ + PTS_ITA_COMP_FUNC_NAME_IMA = 0x0003, +}; + +extern enum_name_t *pts_ita_comp_func_names; + +#endif /** PTS_ITA_COMP_FUNC_NAME_H_ @}*/ diff --git a/src/libpts/pts/components/ita/ita_comp_ima.c b/src/libpts/pts/components/ita/ita_comp_ima.c new file mode 100644 index 000000000..a7da76651 --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_ima.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2011 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 "ita_comp_ima.h" +#include "ita_comp_func_name.h" + +#include "libpts.h" +#include "pts/components/pts_component.h" + +#include <debug.h> +#include <pen/pen.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#define IMA_SECURITY_DIR "/sys/kernel/security/tpm0/" +#define IMA_BIOS_MEASUREMENT_PATH IMA_SECURITY_DIR "binary_bios_measurements" +#define IMA_PCR_MAX 16 + +typedef struct pts_ita_comp_ima_t pts_ita_comp_ima_t; + +/** + * Private data of a pts_ita_comp_ima_t object. + * + */ +struct pts_ita_comp_ima_t { + + /** + * Public pts_component_t interface. + */ + pts_component_t public; + + /** + * Component Functional Name + */ + pts_comp_func_name_t *name; + + /** + * AIK keyid + */ + chunk_t keyid; + + /** + * Sub-component depth + */ + u_int32_t depth; + + /** + * PTS measurement database + */ + pts_database_t *pts_db; + + /** + * Primary key for Component Functional Name database entry + */ + int cid; + + /** + * Primary key for AIK database entry + */ + int kid; + + /** + * Component is registering measurements + */ + bool is_registering; + + /** + * IMA BIOS measurement time + */ + time_t bios_measurement_time; + + /** + * IMA BIOS measurements + */ + linked_list_t *list; + + /** + * Expected measurement count + */ + int count; + + /** + * Measurement sequence number + */ + int seq_no; + + /** + * Shadow PCR registers + */ + chunk_t pcrs[IMA_PCR_MAX]; +}; + +typedef struct entry_t entry_t; + +/** + * Linux IMA measurement entry + */ +struct entry_t { + + /** + * PCR register + */ + u_int32_t pcr; + + /** + * SHA1 measurement hash + */ + chunk_t measurement; +}; + +/** + * Free an entry_t object + */ +static void free_entry(entry_t *this) +{ + free(this->measurement.ptr); + free(this); +} + +/** + * Load a PCR measurement file and determine the creation date + */ +static bool load_measurements(char *file, linked_list_t *list, time_t *created) +{ + u_int32_t pcr, num, len; + entry_t *entry; + struct stat st; + ssize_t res; + int fd; + + fd = open(file, O_RDONLY); + if (fd == -1) + { + DBG1(DBG_PTS, " opening '%s' failed: %s", file, strerror(errno)); + return FALSE; + } + + if (fstat(fd, &st) == -1) + { + DBG1(DBG_PTS, " getting statistics of '%s' failed: %s", file, + strerror(errno)); + close(fd); + return FALSE; + } + *created = st.st_ctime; + + while (TRUE) + { + res = read(fd, &pcr, 4); + if (res == 0) + { + DBG2(DBG_PTS, "loaded bios measurements '%s' (%d entries)", + file, list->get_count(list)); + close(fd); + return TRUE; + } + + entry = malloc_thing(entry_t); + entry->pcr = pcr; + entry->measurement = chunk_alloc(HASH_SIZE_SHA1); + + if (res != 4) + { + break; + } + if (read(fd, &num, 4) != 4) + { + break; + } + if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1) + { + break; + } + if (read(fd, &len, 4) != 4) + { + break; + } + if (lseek(fd, len, SEEK_CUR) == -1) + { + break; + } + list->insert_last(list, entry); + } + + DBG1(DBG_PTS, "loading bios measurements '%s' failed: %s", + file, strerror(errno)); + close(fd); + return FALSE; +} + +METHOD(pts_component_t, get_comp_func_name, pts_comp_func_name_t*, + pts_ita_comp_ima_t *this) +{ + return this->name; +} + +METHOD(pts_component_t, get_evidence_flags, u_int8_t, + pts_ita_comp_ima_t *this) +{ + return PTS_REQ_FUNC_COMP_EVID_PCR; +} + +METHOD(pts_component_t, get_depth, u_int32_t, + pts_ita_comp_ima_t *this) +{ + return this->depth; +} + +METHOD(pts_component_t, measure, status_t, + pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t **evidence) +{ + pts_comp_evidence_t *evid; + chunk_t pcr_before, pcr_after; + pts_pcr_transform_t pcr_transform; + pts_meas_algorithms_t hash_algo; + size_t pcr_len; + entry_t *entry; + hasher_t *hasher; + + hash_algo = PTS_MEAS_ALGO_SHA1; + pcr_len = pts->get_pcr_len(pts); + pcr_transform = pts_meas_algo_to_pcr_transform(hash_algo, pcr_len); + + if (this->list->get_count(this->list) == 0) + { + if (!load_measurements(IMA_BIOS_MEASUREMENT_PATH, this->list, + &this->bios_measurement_time)) + { + return FAILED; + } + } + + if (this->list->remove_first(this->list, (void**)&entry) != SUCCESS) + { + DBG1(DBG_PTS, "could not retrieve measurement entry"); + return FAILED; + } + + pcr_before = chunk_clone(this->pcrs[entry->pcr]); + + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + hasher->get_hash(hasher, pcr_before, NULL); + hasher->get_hash(hasher, entry->measurement, this->pcrs[entry->pcr].ptr); + hasher->destroy(hasher); + + pcr_after = chunk_clone(this->pcrs[entry->pcr]); + + evid = *evidence = pts_comp_evidence_create(this->name->clone(this->name), + this->depth, entry->pcr, hash_algo, pcr_transform, + this->bios_measurement_time, entry->measurement); + evid->set_pcr_info(evid, pcr_before, pcr_after); + + free(entry); + + return (this->list->get_count(this->list)) ? NEED_MORE : SUCCESS; +} + +METHOD(pts_component_t, verify, status_t, + pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t *evidence) +{ + bool has_pcr_info; + u_int32_t extended_pcr, vid, name; + enum_name_t *names; + pts_meas_algorithms_t algo; + pts_pcr_transform_t transform; + time_t measurement_time; + chunk_t measurement, pcr_before, pcr_after; + + measurement = evidence->get_measurement(evidence, &extended_pcr, + &algo, &transform, &measurement_time); + + if (!this->keyid.ptr) + { + if (!pts->get_aik_keyid(pts, &this->keyid)) + { + return FAILED; + } + this->keyid = chunk_clone(this->keyid); + + if (!this->pts_db) + { + DBG1(DBG_PTS, "pts database not available"); + return FAILED; + } + if (this->pts_db->get_comp_measurement_count(this->pts_db, + this->name, this->keyid, algo, + &this->cid, &this->kid, &this->count) != SUCCESS) + { + return FAILED; + } + vid = this->name->get_vendor_id(this->name); + name = this->name->get_name(this->name); + names = pts_components->get_comp_func_names(pts_components, vid); + + if (this->count) + { + DBG1(DBG_PTS, "checking %d %N '%N' functional component evidence " + "measurements", this->count, pen_names, vid, names, name); + } + else + { + DBG1(DBG_PTS, "registering %N '%N' functional component evidence " + "measurements", pen_names, vid, names, name); + this->is_registering = TRUE; + } + } + + if (this->is_registering) + { + if (this->pts_db->insert_comp_measurement(this->pts_db, measurement, + this->cid, this->kid, ++this->seq_no, + extended_pcr, algo) != SUCCESS) + { + return FAILED; + } + this->count = this->seq_no + 1; + } + else + { + if (this->pts_db->check_comp_measurement(this->pts_db, measurement, + this->cid, this->kid, ++this->seq_no, + extended_pcr, algo) != SUCCESS) + { + return FAILED; + } + } + + has_pcr_info = evidence->get_pcr_info(evidence, &pcr_before, &pcr_after); + if (has_pcr_info) + { + if (!pts->add_pcr(pts, extended_pcr, pcr_before, pcr_after)) + { + return FAILED; + } + } + + return (this->seq_no < this->count) ? NEED_MORE : SUCCESS; +} + +METHOD(pts_component_t, check_off_registrations, bool, + pts_ita_comp_ima_t *this) +{ + u_int32_t vid, name; + enum_name_t *names; + + if (!this->is_registering) + { + return FALSE; + } + + /* Finalize registration */ + this->is_registering = FALSE; + + vid = this->name->get_vendor_id(this->name); + name = this->name->get_name(this->name); + names = pts_components->get_comp_func_names(pts_components, vid); + DBG1(DBG_PTS, "registered %d %N '%N' functional component evidence " + "measurements", this->seq_no, pen_names, vid, names, name); + return TRUE; +} + +METHOD(pts_component_t, destroy, void, + pts_ita_comp_ima_t *this) +{ + int i, count; + u_int32_t vid, name; + enum_name_t *names; + + for (i = 0; i < IMA_PCR_MAX; i++) + { + free(this->pcrs[i].ptr); + } + if (this->is_registering) + { + count = this->pts_db->delete_comp_measurements(this->pts_db, + this->cid, this->kid); + vid = this->name->get_vendor_id(this->name); + name = this->name->get_name(this->name); + names = pts_components->get_comp_func_names(pts_components, vid); + DBG1(DBG_PTS, "deleted %d registered %N '%N' functional component " + "evidence measurements", count, pen_names, vid, names, name); + } + this->list->destroy_function(this->list, (void *)free_entry); + this->name->destroy(this->name); + free(this->keyid.ptr); + free(this); +} + +/** + * See header + */ +pts_component_t *pts_ita_comp_ima_create(u_int8_t qualifier, u_int32_t depth, + pts_database_t *pts_db) +{ + pts_ita_comp_ima_t *this; + int i; + + INIT(this, + .public = { + .get_comp_func_name = _get_comp_func_name, + .get_evidence_flags = _get_evidence_flags, + .get_depth = _get_depth, + .measure = _measure, + .verify = _verify, + .check_off_registrations = _check_off_registrations, + .destroy = _destroy, + }, + .name = pts_comp_func_name_create(PEN_ITA, PTS_ITA_COMP_FUNC_NAME_IMA, + qualifier), + .depth = depth, + .pts_db = pts_db, + .list = linked_list_create(), + ); + + for (i = 0; i < IMA_PCR_MAX; i++) + { + this->pcrs[i] = chunk_alloc(HASH_SIZE_SHA1); + memset(this->pcrs[i].ptr, 0x00, HASH_SIZE_SHA1); + } + return &this->public; +} + diff --git a/src/libpts/pts/components/ita/ita_comp_ima.h b/src/libpts/pts/components/ita/ita_comp_ima.h new file mode 100644 index 000000000..1ca27e6f0 --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_ima.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 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 pts_ita_comp_func_name pts_ita_comp_func_name + * @{ @ingroup pts + */ + +#ifndef PTS_ITA_COMP_IMA_H_ +#define PTS_ITA_COMP_IMA_H_ + +#include "pts/components/pts_component.h" + +/** + * Create a PTS ITS Functional Component object + * + * @param qualifier PTS Component Functional Name Qualifier + * @param depth Sub-component depth + * @param pts_db PTS measurement database + */ +pts_component_t* pts_ita_comp_ima_create(u_int8_t qualifier, u_int32_t depth, + pts_database_t *pts_db); + +#endif /** PTS_ITA_COMP_IMA_H_ @}*/ diff --git a/src/libpts/pts/components/ita/ita_comp_tboot.c b/src/libpts/pts/components/ita/ita_comp_tboot.c new file mode 100644 index 000000000..287aae727 --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_tboot.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2011 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 "ita_comp_tboot.h" +#include "ita_comp_func_name.h" + +#include "libpts.h" +#include "pts/components/pts_component.h" + +#include <debug.h> +#include <pen/pen.h> + +typedef struct pts_ita_comp_tboot_t pts_ita_comp_tboot_t; + +/** + * Private data of a pts_ita_comp_tboot_t object. + * + */ +struct pts_ita_comp_tboot_t { + + /** + * Public pts_component_t interface. + */ + pts_component_t public; + + /** + * Component Functional Name + */ + pts_comp_func_name_t *name; + + /** + * AIK keyid + */ + chunk_t keyid; + + /** + * Sub-component depth + */ + u_int32_t depth; + + /** + * PTS measurement database + */ + pts_database_t *pts_db; + + /** + * Primary key for Component Functional Name database entry + */ + int cid; + + /** + * Primary key for AIK database entry + */ + int kid; + + /** + * Component is registering measurements + */ + bool is_registering; + + /** + * Time of TBOOT measurement + */ + time_t measurement_time; + + /** + * Expected measurement count + */ + int count; + + /** + * Measurement sequence number + */ + int seq_no; + +}; + +METHOD(pts_component_t, get_comp_func_name, pts_comp_func_name_t*, + pts_ita_comp_tboot_t *this) +{ + return this->name; +} + +METHOD(pts_component_t, get_evidence_flags, u_int8_t, + pts_ita_comp_tboot_t *this) +{ + return PTS_REQ_FUNC_COMP_EVID_PCR; +} + +METHOD(pts_component_t, get_depth, u_int32_t, + pts_ita_comp_tboot_t *this) +{ + return this->depth; +} + +METHOD(pts_component_t, measure, status_t, + pts_ita_comp_tboot_t *this, pts_t *pts, pts_comp_evidence_t **evidence) +{ + pts_comp_evidence_t *evid; + char *meas_hex, *pcr_before_hex, *pcr_after_hex; + chunk_t measurement, pcr_before, pcr_after; + size_t hash_size, pcr_len; + u_int32_t extended_pcr; + pts_pcr_transform_t pcr_transform; + pts_meas_algorithms_t hash_algo; + + switch (this->seq_no++) + { + case 0: + /* dummy data since currently the TBOOT log is not retrieved */ + time(&this->measurement_time); + meas_hex = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.pcr17_meas", NULL); + pcr_before_hex = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.pcr17_before", NULL); + pcr_after_hex = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.pcr17_after", NULL); + extended_pcr = PCR_TBOOT_POLICY; + break; + case 1: + /* dummy data since currently the TBOOT log is not retrieved */ + meas_hex = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.pcr18_meas", NULL); + pcr_before_hex = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.pcr18_before", NULL); + pcr_after_hex = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.pcr18_after", NULL); + extended_pcr = PCR_TBOOT_MLE; + break; + default: + return FAILED; + } + + hash_algo = pts->get_meas_algorithm(pts); + hash_size = pts_meas_algo_hash_size(hash_algo); + pcr_len = pts->get_pcr_len(pts); + pcr_transform = pts_meas_algo_to_pcr_transform(hash_algo, pcr_len); + + /* get and check the measurement data */ + measurement = chunk_from_hex( + chunk_create(meas_hex, strlen(meas_hex)), NULL); + pcr_before = chunk_from_hex( + chunk_create(pcr_before_hex, strlen(pcr_before_hex)), NULL); + pcr_after = chunk_from_hex( + chunk_create(pcr_after_hex, strlen(pcr_after_hex)), NULL); + if (pcr_before.len != pcr_len || pcr_after.len != pcr_len || + measurement.len != hash_size) + { + DBG1(DBG_PTS, "TBOOT measurement or pcr data have the wrong size"); + free(measurement.ptr); + free(pcr_before.ptr); + free(pcr_after.ptr); + return FAILED; + } + + evid = *evidence = pts_comp_evidence_create(this->name->clone(this->name), + this->depth, extended_pcr, + hash_algo, pcr_transform, + this->measurement_time, measurement); + evid->set_pcr_info(evid, pcr_before, pcr_after); + + return (this->seq_no < 2) ? NEED_MORE : SUCCESS; +} + +METHOD(pts_component_t, verify, status_t, + pts_ita_comp_tboot_t *this, pts_t *pts, pts_comp_evidence_t *evidence) +{ + bool has_pcr_info; + u_int32_t extended_pcr, vid, name; + enum_name_t *names; + pts_meas_algorithms_t algo; + pts_pcr_transform_t transform; + time_t measurement_time; + chunk_t measurement, pcr_before, pcr_after; + + measurement = evidence->get_measurement(evidence, &extended_pcr, + &algo, &transform, &measurement_time); + + if (!this->keyid.ptr) + { + if (!pts->get_aik_keyid(pts, &this->keyid)) + { + return FAILED; + } + this->keyid = chunk_clone(this->keyid); + + if (!this->pts_db) + { + DBG1(DBG_PTS, "pts database not available"); + return FAILED; + } + if (this->pts_db->get_comp_measurement_count(this->pts_db, + this->name, this->keyid, algo, + &this->cid, &this->kid, &this->count) != SUCCESS) + { + return FAILED; + } + vid = this->name->get_vendor_id(this->name); + name = this->name->get_name(this->name); + names = pts_components->get_comp_func_names(pts_components, vid); + + if (this->count) + { + DBG1(DBG_PTS, "checking %d %N '%N' functional component evidence " + "measurements", this->count, pen_names, vid, names, name); + } + else + { + DBG1(DBG_PTS, "registering %N '%N' functional component evidence " + "measurements", pen_names, vid, names, name); + this->is_registering = TRUE; + } + } + + if (this->is_registering) + { + if (this->pts_db->insert_comp_measurement(this->pts_db, measurement, + this->cid, this->kid, ++this->seq_no, + extended_pcr, algo) != SUCCESS) + { + return FAILED; + } + this->count = this->seq_no + 1; + } + else + { + if (this->pts_db->check_comp_measurement(this->pts_db, measurement, + this->cid, this->kid, ++this->seq_no, + extended_pcr, algo) != SUCCESS) + { + return FAILED; + } + } + + has_pcr_info = evidence->get_pcr_info(evidence, &pcr_before, &pcr_after); + if (has_pcr_info) + { + if (!pts->add_pcr(pts, extended_pcr, pcr_before, pcr_after)) + { + return FAILED; + } + } + + return (this->seq_no < this->count) ? NEED_MORE : SUCCESS; +} + +METHOD(pts_component_t, check_off_registrations, bool, + pts_ita_comp_tboot_t *this) +{ + u_int32_t vid, name; + enum_name_t *names; + + if (!this->is_registering) + { + return FALSE; + } + + /* Finalize registration */ + this->is_registering = FALSE; + + vid = this->name->get_vendor_id(this->name); + name = this->name->get_name(this->name); + names = pts_components->get_comp_func_names(pts_components, vid); + DBG1(DBG_PTS, "registered %d %N '%N' functional component evidence " + "measurements", this->seq_no, pen_names, vid, names, name); + return TRUE; +} + +METHOD(pts_component_t, destroy, void, + pts_ita_comp_tboot_t *this) +{ + int count; + u_int32_t vid, name; + enum_name_t *names; + + if (this->is_registering) + { + count = this->pts_db->delete_comp_measurements(this->pts_db, + this->cid, this->kid); + vid = this->name->get_vendor_id(this->name); + name = this->name->get_name(this->name); + names = pts_components->get_comp_func_names(pts_components, vid); + DBG1(DBG_PTS, "deleted %d registered %N '%N' functional component " + "evidence measurements", count, pen_names, vid, names, name); + } + this->name->destroy(this->name); + free(this->keyid.ptr); + free(this); +} + +/** + * See header + */ +pts_component_t *pts_ita_comp_tboot_create(u_int8_t qualifier, u_int32_t depth, + pts_database_t *pts_db) +{ + pts_ita_comp_tboot_t *this; + + INIT(this, + .public = { + .get_comp_func_name = _get_comp_func_name, + .get_evidence_flags = _get_evidence_flags, + .get_depth = _get_depth, + .measure = _measure, + .verify = _verify, + .check_off_registrations = _check_off_registrations, + .destroy = _destroy, + }, + .name = pts_comp_func_name_create(PEN_ITA, PTS_ITA_COMP_FUNC_NAME_TBOOT, + qualifier), + .depth = depth, + .pts_db = pts_db, + ); + + return &this->public; +} + diff --git a/src/libpts/pts/components/ita/ita_comp_tboot.h b/src/libpts/pts/components/ita/ita_comp_tboot.h new file mode 100644 index 000000000..39554fbc7 --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_tboot.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_ita_comp_func_name pts_ita_comp_func_name + * @{ @ingroup pts + */ + +#ifndef PTS_ITA_COMP_TBOOT_H_ +#define PTS_ITA_COMP_TBOOT_H_ + +#include "pts/components/pts_component.h" + +/** + * Create a PTS ITS Functional Component object + * + * @param qualifier PTS Component Functional Name Qualifier + * @param depth Sub-component depth + * @param pts_db PTS measurement database + */ +pts_component_t* pts_ita_comp_tboot_create(u_int8_t qualifier, u_int32_t depth, + pts_database_t *pts_db); + +#endif /** PTS_ITA_COMP_TBOOT_H_ @}*/ diff --git a/src/libpts/pts/components/ita/ita_comp_tgrub.c b/src/libpts/pts/components/ita/ita_comp_tgrub.c new file mode 100644 index 000000000..0dfd5fd41 --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_tgrub.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2011 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 "ita_comp_tgrub.h" +#include "ita_comp_func_name.h" + +#include "pts/components/pts_component.h" + +#include <debug.h> +#include <pen/pen.h> + +typedef struct pts_ita_comp_tgrub_t pts_ita_comp_tgrub_t; + +/** + * Private data of a pts_ita_comp_tgrub_t object. + * + */ +struct pts_ita_comp_tgrub_t { + + /** + * Public pts_component_t interface. + */ + pts_component_t public; + + /** + * Component Functional Name + */ + pts_comp_func_name_t *name; + + /** + * Sub-component depth + */ + u_int32_t depth; + + /** + * PTS measurement database + */ + pts_database_t *pts_db; + +}; + +METHOD(pts_component_t, get_comp_func_name, pts_comp_func_name_t*, + pts_ita_comp_tgrub_t *this) +{ + return this->name; +} + +METHOD(pts_component_t, get_evidence_flags, u_int8_t, + pts_ita_comp_tgrub_t *this) +{ + return PTS_REQ_FUNC_COMP_EVID_PCR; +} + +METHOD(pts_component_t, get_depth, u_int32_t, + pts_ita_comp_tgrub_t *this) +{ + return this->depth; +} + +METHOD(pts_component_t, measure, status_t, + pts_ita_comp_tgrub_t *this, pts_t *pts, pts_comp_evidence_t **evidence) +{ + pts_comp_evidence_t *evid; + u_int32_t extended_pcr; + time_t measurement_time; + chunk_t measurement, pcr_before, pcr_after; + pts_pcr_transform_t pcr_transform; + pts_meas_algorithms_t hash_algo; + size_t hash_size, pcr_len; + + /* Provisional implementation for TGRUB */ + extended_pcr = PCR_DEBUG; + time(&measurement_time); + + if (!pts->read_pcr(pts, extended_pcr, &pcr_after)) + { + DBG1(DBG_PTS, "error occurred while reading PCR: %d", extended_pcr); + return FAILED; + } + + hash_algo = pts->get_meas_algorithm(pts); + hash_size = pts_meas_algo_hash_size(hash_algo); + pcr_len = pts->get_pcr_len(pts); + pcr_transform = pts_meas_algo_to_pcr_transform(hash_algo, pcr_len); + + measurement = chunk_alloc(hash_size); + memset(measurement.ptr, 0x00, measurement.len); + + pcr_before = chunk_alloc(pcr_len); + memset(pcr_before.ptr, 0x00, pcr_before.len); + + evid = *evidence = pts_comp_evidence_create(this->name->clone(this->name), + this->depth, extended_pcr, + hash_algo, pcr_transform, + measurement_time, measurement); + evid->set_pcr_info(evid, pcr_before, pcr_after); + + return SUCCESS; +} + +METHOD(pts_component_t, verify, status_t, + pts_ita_comp_tgrub_t *this, pts_t *pts, pts_comp_evidence_t *evidence) +{ + bool has_pcr_info; + u_int32_t extended_pcr; + pts_meas_algorithms_t algo; + pts_pcr_transform_t transform; + time_t measurement_time; + chunk_t measurement, pcr_before, pcr_after; + + measurement = evidence->get_measurement(evidence, &extended_pcr, + &algo, &transform, &measurement_time); + if (extended_pcr != PCR_DEBUG) + { + return FAILED; + } + + /* TODO check measurement in database */ + + has_pcr_info = evidence->get_pcr_info(evidence, &pcr_before, &pcr_after); + if (has_pcr_info) + { + if (!pts->add_pcr(pts, extended_pcr, pcr_before, pcr_after)) + { + return FAILED; + } + } + + return SUCCESS; +} + +METHOD(pts_component_t, check_off_registrations, bool, + pts_ita_comp_tgrub_t *this) +{ + return FALSE; +} + +METHOD(pts_component_t, destroy, void, + pts_ita_comp_tgrub_t *this) +{ + this->name->destroy(this->name); + free(this); +} + +/** + * See header + */ +pts_component_t *pts_ita_comp_tgrub_create(u_int8_t qualifier, u_int32_t depth, + pts_database_t *pts_db) +{ + pts_ita_comp_tgrub_t *this; + + INIT(this, + .public = { + .get_comp_func_name = _get_comp_func_name, + .get_evidence_flags = _get_evidence_flags, + .get_depth = _get_depth, + .measure = _measure, + .verify = _verify, + .check_off_registrations = _check_off_registrations, + .destroy = _destroy, + }, + .name = pts_comp_func_name_create(PEN_ITA, PTS_ITA_COMP_FUNC_NAME_TGRUB, + qualifier), + .depth = depth, + .pts_db = pts_db, + ); + + return &this->public; +} + diff --git a/src/libpts/pts/components/ita/ita_comp_tgrub.h b/src/libpts/pts/components/ita/ita_comp_tgrub.h new file mode 100644 index 000000000..52ecc325c --- /dev/null +++ b/src/libpts/pts/components/ita/ita_comp_tgrub.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_ita_comp_func_name pts_ita_comp_func_name + * @{ @ingroup pts + */ + +#ifndef PTS_ITA_COMP_TGRUB_H_ +#define PTS_ITA_COMP_TGRUB_H_ + +#include "pts/components/pts_component.h" + +/** + * Create a PTS ITS Functional Component object + * + * @param qualifier PTS Component Functional Name Qualifier + * @param depth Sub-component depth + * @param pts_db PTS measurement database + */ +pts_component_t* pts_ita_comp_tgrub_create(u_int8_t qualifier, u_int32_t depth, + pts_database_t *pts_db); + +#endif /** PTS_ITA_COMP_TGRUB_H_ @}*/ diff --git a/src/libpts/pts/components/pts_comp_evidence.c b/src/libpts/pts/components/pts_comp_evidence.c new file mode 100644 index 000000000..9eb8dae75 --- /dev/null +++ b/src/libpts/pts/components/pts_comp_evidence.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu, 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 "pts/components/pts_comp_evidence.h" + +#include <debug.h> + +typedef struct private_pts_comp_evidence_t private_pts_comp_evidence_t; + +/** + * Private data of a pts_comp_evidence_t object. + */ +struct private_pts_comp_evidence_t { + + /** + * Public pts_comp_evidence_t interface. + */ + pts_comp_evidence_t public; + + /** + * Component Functional Name + */ + pts_comp_func_name_t *name; + + /** + * Sub-Component Depth + */ + u_int32_t depth; + + /** + * Measurement Time + */ + time_t measurement_time; + + /** + * Measurement Time + */ + chunk_t measurement; + + /** + * Measurement Hash Algorithm + */ + pts_meas_algorithms_t hash_algorithm; + + /** + * Is PCR Information included? + */ + bool has_pcr_info; + + /** + * PCR the measurement was extended into + */ + u_int32_t extended_pcr; + + /** + * PCR value before extension + */ + chunk_t pcr_before; + + /** + * PCR value after extension + */ + chunk_t pcr_after; + + /** + * Transformation used for extending measurement into PCR + */ + pts_pcr_transform_t transform; + + /** + * Component Validation Result + */ + pts_comp_evid_validation_t validation; + + /** + * Verification Policy URI + */ + chunk_t policy_uri; + +}; + +METHOD(pts_comp_evidence_t, get_comp_func_name, pts_comp_func_name_t*, + private_pts_comp_evidence_t *this, u_int32_t *depth) +{ + if (depth) + { + *depth = this->depth; + } + return this->name; +} + +METHOD(pts_comp_evidence_t, get_extended_pcr, u_int32_t, + private_pts_comp_evidence_t *this) +{ + return this->extended_pcr; +} + +METHOD(pts_comp_evidence_t, get_measurement, chunk_t, + private_pts_comp_evidence_t *this, u_int32_t *extended_pcr, + pts_meas_algorithms_t *algo, pts_pcr_transform_t *transform, + time_t *measurement_time) +{ + if (extended_pcr) + { + *extended_pcr = this->extended_pcr; + } + if (algo) + { + *algo = this->hash_algorithm; + } + if (transform) + { + *transform = this->transform; + } + if (measurement_time) + { + *measurement_time = this->measurement_time; + } + return this->measurement; +} + +METHOD(pts_comp_evidence_t, get_pcr_info, bool, + private_pts_comp_evidence_t *this, chunk_t *pcr_before, chunk_t *pcr_after) +{ + if (pcr_before) + { + *pcr_before = this->pcr_before; + } + if (pcr_after) + { + *pcr_after = this->pcr_after; + } + return this->has_pcr_info; +} + +METHOD(pts_comp_evidence_t, set_pcr_info, void, + private_pts_comp_evidence_t *this, chunk_t pcr_before, chunk_t pcr_after) +{ + this->has_pcr_info = TRUE; + this->pcr_before = pcr_before; + this->pcr_after = pcr_after; + + DBG2(DBG_PTS, "PCR %2d before value : %#B", this->extended_pcr, &pcr_before); + DBG2(DBG_PTS, "PCR %2d after value : %#B", this->extended_pcr, &pcr_after); +} + +METHOD(pts_comp_evidence_t, get_validation, pts_comp_evid_validation_t, + private_pts_comp_evidence_t *this, chunk_t *uri) +{ + if (uri) + { + *uri = this->policy_uri; + } + return this->validation; +} + +METHOD(pts_comp_evidence_t, set_validation, void, + private_pts_comp_evidence_t *this, pts_comp_evid_validation_t validation, + chunk_t uri) +{ + this->validation = validation; + this->policy_uri = chunk_clone(uri); +} + +METHOD(pts_comp_evidence_t, destroy, void, + private_pts_comp_evidence_t *this) +{ + this->name->destroy(this->name); + free(this->measurement.ptr); + free(this->pcr_before.ptr); + free(this->pcr_after.ptr); + free(this->policy_uri.ptr); + free(this); +} + +/** + * See header + */ +pts_comp_evidence_t *pts_comp_evidence_create(pts_comp_func_name_t *name, + u_int32_t depth, + u_int32_t extended_pcr, + pts_meas_algorithms_t algo, + pts_pcr_transform_t transform, + time_t measurement_time, + chunk_t measurement) +{ + private_pts_comp_evidence_t *this; + + INIT(this, + .public = { + .get_comp_func_name = _get_comp_func_name, + .get_extended_pcr = _get_extended_pcr, + .get_measurement = _get_measurement, + .get_pcr_info = _get_pcr_info, + .set_pcr_info = _set_pcr_info, + .get_validation = _get_validation, + .set_validation = _set_validation, + .destroy = _destroy, + }, + .name = name, + .depth = depth, + .extended_pcr = extended_pcr, + .hash_algorithm = algo, + .transform = transform, + .measurement_time = measurement_time, + .measurement = measurement, + ); + + name->log(name, ""); + DBG2(DBG_PTS, "measurement time: %T", &measurement_time, FALSE); + DBG2(DBG_PTS, "PCR %2d extended with: %#B", extended_pcr, &measurement); + + return &this->public; +} + +/** + * See header + */ +pts_pcr_transform_t pts_meas_algo_to_pcr_transform(pts_meas_algorithms_t algo, + size_t pcr_len) +{ + size_t hash_size; + + hash_size = pts_meas_algo_hash_size(algo); + if (hash_size == 0) + { + return PTS_PCR_TRANSFORM_NO; + } + if (hash_size == pcr_len) + { + return PTS_PCR_TRANSFORM_MATCH; + } + if (hash_size > pcr_len) + { + return PTS_PCR_TRANSFORM_LONG; + } + return PTS_PCR_TRANSFORM_SHORT; +} + diff --git a/src/libpts/pts/components/pts_comp_evidence.h b/src/libpts/pts/components/pts_comp_evidence.h new file mode 100644 index 000000000..fe86aa940 --- /dev/null +++ b/src/libpts/pts/components/pts_comp_evidence.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu, 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 pts_comp_evidence pts_comp_evidence + * @{ @ingroup pts + */ + +#ifndef PTS_COMP_EVIDENCE_H_ +#define PTS_COMP_EVIDENCE_H_ + +typedef struct pts_comp_evidence_t pts_comp_evidence_t; +typedef enum pts_pcr_transform_t pts_pcr_transform_t; +typedef enum pts_comp_evid_validation_t pts_comp_evid_validation_t; + +#include "pts/pts_meas_algo.h" +#include "pts/components/pts_comp_func_name.h" + +#include <library.h> + +/** + * PTS PCR Transformations + */ +enum pts_pcr_transform_t { + /** No Transformation */ + PTS_PCR_TRANSFORM_NO = 0, + /** Hash Value matched PCR size */ + PTS_PCR_TRANSFORM_MATCH = 1, + /** Hash value shorter than PCR size */ + PTS_PCR_TRANSFORM_SHORT = 2, + /** Hash value longer than PCR size */ + PTS_PCR_TRANSFORM_LONG = 3, +}; + +/** + * PTS Component Evidence Validation Result Flags + */ +enum pts_comp_evid_validation_t { + /** No Validation was attempted */ + PTS_COMP_EVID_VALIDATION_NONE = 0x00, + /** Attempted validation, unable to verify */ + PTS_COMP_EVID_VALIDATION_UNABLE = 0x20, + /** Attempted validation, verification failed */ + PTS_COMP_EVID_VALIDATION_FAILED = 0x40, + /** Attempted validation, verification passed */ + PTS_COMP_EVID_VALIDATION_PASSED = 0x60, +}; + +/** + * PTS Functional Component Interface + */ +struct pts_comp_evidence_t { + + /** + * Gets the Component Functional Name and Sub-Component Depth + * + * @param depth Sub-Component Depth + * @result Component Functional Name + */ + pts_comp_func_name_t* (*get_comp_func_name)(pts_comp_evidence_t *this, + u_int32_t *depth); + + /** + * Gets the PCR the measurement was extended into + * + * @result PCR the measurement was extended into + */ + u_int32_t (*get_extended_pcr)(pts_comp_evidence_t *this); + + /** + * Gets the measurement and the algorithms used + * + * @param extended_pcr PCR the measurement was extended into + * @param algo Measurement hash algorithm + * @param transform Transformation used for PCR extension + * @param measurement_time Time the measurement was taken + * @result Measurement hash value + */ + chunk_t (*get_measurement)(pts_comp_evidence_t *this, + u_int32_t *extended_pcr, + pts_meas_algorithms_t *algo, + pts_pcr_transform_t *transform, + time_t *measurement_time); + + /** + * Gets the PCR information if available + * + * @param pcr_before PCR value before extension + * @param pcr_after PCR value after extension + * @result TRUE if PCR information is available + */ + bool (*get_pcr_info)(pts_comp_evidence_t *this, chunk_t *pcr_before, + chunk_t *pcr_after); + + /** + * Sets PCR information if available + * + * @param pcr_before PCR value before extension + * @param pcr_after PCR value after extension + */ + void (*set_pcr_info)(pts_comp_evidence_t *this, chunk_t pcr_before, + chunk_t pcr_after); + + /** + * Gets Validation Result if available + * + * @param uri Verification Policy URI + * @return validation Validation Result + */ + pts_comp_evid_validation_t (*get_validation)(pts_comp_evidence_t *this, + chunk_t *uri); + + /** + * Sets Validation Result if available + * + * @param validation Validation Result + * @param uri Verification Policy URI + */ + void (*set_validation)(pts_comp_evidence_t *this, + pts_comp_evid_validation_t validation, chunk_t uri); + + /** + * Destroys a pts_comp_evidence_t object. + */ + void (*destroy)(pts_comp_evidence_t *this); + +}; + +/** + * Creates a pts_comp_evidence_t object + * + * @param name Component Functional Name + * @param depth Sub-component depth + * @param extended_pcr PCR the measurement was extended into + * @param algo Measurement hash algorithm + * @param transform Transformation used for PCR extension + * @param measurement_time Time the measurement was taken, 0 if unknown + * @param measurement Measurement hash value + */ +pts_comp_evidence_t* pts_comp_evidence_create(pts_comp_func_name_t *name, + u_int32_t depth, + u_int32_t extended_pcr, + pts_meas_algorithms_t algo, + pts_pcr_transform_t transform, + time_t measurement_time, + chunk_t measurement); + +/** + * Determine transform to fit measurement hash into PCR register + * + * @param algo Measurement hash algorithm + * @param pcr_len Length of the PCR registers in bytes + * @return PCR transform type + */ +pts_pcr_transform_t pts_meas_algo_to_pcr_transform(pts_meas_algorithms_t algo, + size_t pcr_len); + +#endif /** PTS_COMP_EVIDENCE_H_ @}*/ diff --git a/src/libpts/pts/components/pts_comp_func_name.c b/src/libpts/pts/components/pts_comp_func_name.c new file mode 100644 index 000000000..d98850d78 --- /dev/null +++ b/src/libpts/pts/components/pts_comp_func_name.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2011 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 "libpts.h" +#include "pts/components/pts_comp_func_name.h" + +#include <debug.h> + +typedef struct private_pts_comp_func_name_t private_pts_comp_func_name_t; + +/** + * Private data of a pts_comp_func_name_t object. + * + */ +struct private_pts_comp_func_name_t { + + /** + * Public pts_comp_func_name_t interface. + */ + pts_comp_func_name_t public; + + /** + * PTS Component Functional Name Vendor ID + */ + u_int32_t vid; + + /** + * PTS Component Functional Name + */ + u_int32_t name; + + /** + * PTS Component Functional Name Qualifier + */ + u_int8_t qualifier; + +}; + +METHOD(pts_comp_func_name_t, get_vendor_id, u_int32_t, + private_pts_comp_func_name_t *this) +{ + return this->vid; +} + +METHOD(pts_comp_func_name_t, get_name, u_int32_t, + private_pts_comp_func_name_t *this) +{ + return this->name; +} + +METHOD(pts_comp_func_name_t, get_qualifier, u_int8_t, + private_pts_comp_func_name_t *this) +{ + return this->qualifier; +} + +static bool equals(private_pts_comp_func_name_t *this, + private_pts_comp_func_name_t *other) +{ + if (this->vid != other->vid || this->name != other->name) + { + return FALSE; + } + if (this->qualifier == PTS_QUALIFIER_UNKNOWN || + other->qualifier == PTS_QUALIFIER_UNKNOWN) + { + return TRUE; + } + /* TODO handle qualifier wildcards */ + + return this->qualifier == other->qualifier; +} + +METHOD(pts_comp_func_name_t, clone_, pts_comp_func_name_t*, + private_pts_comp_func_name_t *this) +{ + private_pts_comp_func_name_t *clone; + + clone = malloc_thing(private_pts_comp_func_name_t); + memcpy(clone, this, sizeof(private_pts_comp_func_name_t)); + + return &clone->public; +} + +METHOD(pts_comp_func_name_t, log_, void, + private_pts_comp_func_name_t *this, char *label) +{ + enum_name_t *names, *types; + char flags[8]; + int type; + + names = pts_components->get_comp_func_names(pts_components, this->vid); + types = pts_components->get_qualifier_type_names(pts_components, this->vid); + type = pts_components->get_qualifier(pts_components, &this->public, flags); + + if (names && types) + { + DBG2(DBG_PTS, "%s%N functional component '%N' [%s] '%N'", + label, pen_names, this->vid, names, this->name, flags, types, type); + } + else + { + DBG2(DBG_PTS, "%s0x%06x functional component 0x%08x 0x%02x", + label, this->vid, this->name, this->qualifier); + } +} + +METHOD(pts_comp_func_name_t, destroy, void, + private_pts_comp_func_name_t *this) +{ + free(this); +} + +/** + * See header + */ +pts_comp_func_name_t* pts_comp_func_name_create(u_int32_t vid, u_int32_t name, + u_int8_t qualifier) +{ + private_pts_comp_func_name_t *this; + + INIT(this, + .public = { + .get_vendor_id = _get_vendor_id, + .get_name = _get_name, + .get_qualifier = _get_qualifier, + .equals = (bool(*)(pts_comp_func_name_t*,pts_comp_func_name_t*))equals, + .clone = _clone_, + .log = _log_, + .destroy = _destroy, + }, + .vid = vid, + .name = name, + .qualifier = qualifier, + ); + + return &this->public; +} + diff --git a/src/libpts/pts/components/pts_comp_func_name.h b/src/libpts/pts/components/pts_comp_func_name.h new file mode 100644 index 000000000..2c7a84177 --- /dev/null +++ b/src/libpts/pts/components/pts_comp_func_name.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_comp_func_name pts_comp_func_name + * @{ @ingroup pts + */ + +#ifndef PTS_FUNC_COMP_NAME_H_ +#define PTS_FUNC_COMP_NAME_H_ + +typedef struct pts_comp_func_name_t pts_comp_func_name_t; + +#include <library.h> + +#define PTS_QUALIFIER_UNKNOWN 0x00 +#define PTS_QUALIFIER_WILDCARD 0x3F + +/** + * PTS Component Functional Name object + */ +struct pts_comp_func_name_t { + + /** + * Get the PTS Component Functional Name Vendor ID + * + * @return PTS Component Functional Name Vendor ID + */ + u_int32_t (*get_vendor_id)(pts_comp_func_name_t *this); + + /** + * Get the PTS Component Functional Name + * + * @return PTS Component Functional Name + */ + u_int32_t (*get_name)(pts_comp_func_name_t *this); + + /** + * Get the PTS Component Functional Name Qualifier + * + * @return PTS Component Functional Name Qualifier + */ + u_int8_t (*get_qualifier)(pts_comp_func_name_t *this); + + /** + * Check to PTS Component Functional Names for equality + * + * @param other Other PTS Component Functional Name + * @return TRUE if equal + */ + bool (*equals)(pts_comp_func_name_t *this, pts_comp_func_name_t *other); + + /** + * Clone a PTS Component Functional Name + * + * @return Cloned PTS Component Functional Name + */ + pts_comp_func_name_t* (*clone)(pts_comp_func_name_t *this); + + /** + * Write PTS Component Functional Name information to the standard logfile + * + * @param label Label added to log output + */ + void (*log)(pts_comp_func_name_t *this, char *label); + + /** + * Destroys a pts_component_t object. + */ + void (*destroy)(pts_comp_func_name_t *this); + +}; + +/** + * Create a PTS Component Functional Name object + * + * @param vid PTS Component Functional Name Vendor ID + * @param name PTS Component Functional Name + * @param PTS Component Functional Name Qualifier + */ +pts_comp_func_name_t* pts_comp_func_name_create(u_int32_t vid, u_int32_t name, + u_int8_t qualifier); + +#endif /** PTS_FUNC_COMP_NAME_H_ @}*/ diff --git a/src/libpts/pts/components/pts_component.h b/src/libpts/pts/components/pts_component.h new file mode 100644 index 000000000..524ff332d --- /dev/null +++ b/src/libpts/pts/components/pts_component.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2011 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 pts_component pts_component + * @{ @ingroup pts + */ + +#ifndef PTS_COMPONENT_H_ +#define PTS_COMPONENT_H_ + +typedef struct pts_component_t pts_component_t; + +#include "pts/pts.h" +#include "pts/pts_database.h" +#include "pts/components/pts_comp_func_name.h" +#include "pts/components/pts_comp_evidence.h" + +#include <library.h> + +/** + * PTS Functional Component Interface + */ +struct pts_component_t { + + /** + * Get the PTS Component Functional Name + * + * @return PTS Component Functional Name + */ + pts_comp_func_name_t* (*get_comp_func_name)(pts_component_t *this); + + /** + * Get the PTS Component Evidence Flags + * + * @return PTS Component Functional Name + */ + u_int8_t (*get_evidence_flags)(pts_component_t *this); + + /** + * Get the PTS Sub-component Depth + * + * @return PTS Sub-component Depth + */ + u_int32_t (*get_depth)(pts_component_t *this); + + /** + * Do evidence measurements on the PTS Functional Component + * + * @param pts PTS interface + * @param evidence returns component evidence measureemt + * @return status return code + */ + status_t (*measure)(pts_component_t *this, pts_t *pts, + pts_comp_evidence_t** evidence); + + /** + * Verify the evidence measurements of the PTS Functional Component + * + * @param pts PTS interface + * @param evidence component evidence measurement to be verified + * @return status return code + */ + status_t (*verify)(pts_component_t *this, pts_t *pts, + pts_comp_evidence_t *evidence); + + + /** + * Tell the PTS Functional Component to finalize pending registrations + * + * @return TRUE if there are pending registrations + */ + bool (*check_off_registrations)(pts_component_t *this); + + /** + * Destroys a pts_component_t object. + */ + void (*destroy)(pts_component_t *this); + +}; + +#endif /** PTS_COMPONENT_H_ @}*/ diff --git a/src/libpts/pts/components/pts_component_manager.c b/src/libpts/pts/components/pts_component_manager.c new file mode 100644 index 000000000..8ac4767bf --- /dev/null +++ b/src/libpts/pts/components/pts_component_manager.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2011 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 "pts/components/pts_component_manager.h" + +#include <utils/linked_list.h> +#include <debug.h> + +typedef struct private_pts_component_manager_t private_pts_component_manager_t; +typedef struct vendor_entry_t vendor_entry_t; +typedef struct component_entry_t component_entry_t; + +#define PTS_QUALIFIER_SIZE 6 + +/** + * Vendor-specific namespace information and list of registered components + */ +struct vendor_entry_t { + + /** + * Vendor ID + */ + pen_t vendor_id; + + /** + * Vendor-specific Component Functional names + */ + enum_name_t *comp_func_names; + + /** + * Vendor-specific Qualifier Type names + */ + enum_name_t *qualifier_type_names; + + /** + * Vendor-specific Qualifier Flag names + */ + char *qualifier_flag_names; + + /** + * Vendor-specific size of Qualfiier Type field + */ + int qualifier_type_size; + + /** + * List of vendor-specific registered Functional Components + */ + linked_list_t *components; +}; + +/** + * Destroy a vendor_entry_t object + */ +static void vendor_entry_destroy(vendor_entry_t *entry) +{ + entry->components->destroy_function(entry->components, free); + free(entry); +} + +/** + * Creation method for a vendor-specific Functional Component + */ +struct component_entry_t { + + /** + * Vendor-Specific Component Functional Name + */ + u_int32_t name; + + /** + * Functional Component creation method + */ + pts_component_create_t create; +}; + +/** + * Private data of a pts_component_manager_t object. + * + */ +struct private_pts_component_manager_t { + + /** + * Public pts_component_manager_t interface. + */ + pts_component_manager_t public; + + /** + * List of vendor-specific namespaces and registered components + */ + linked_list_t *list; +}; + +METHOD(pts_component_manager_t, add_vendor, void, + private_pts_component_manager_t *this, pen_t vendor_id, + enum_name_t *comp_func_names, int qualifier_type_size, + char *qualifier_flag_names, enum_name_t *qualifier_type_names) +{ + vendor_entry_t *entry; + + entry = malloc_thing(vendor_entry_t); + entry->vendor_id = vendor_id; + entry->comp_func_names = comp_func_names; + entry->qualifier_type_size = qualifier_type_size; + entry->qualifier_flag_names = qualifier_flag_names; + entry->qualifier_type_names = qualifier_type_names; + entry->components = linked_list_create(); + + this->list->insert_last(this->list, entry); + DBG2(DBG_PTS, "added %N functional component namespace", + pen_names, vendor_id); +} + +METHOD(pts_component_manager_t, get_comp_func_names, enum_name_t*, + private_pts_component_manager_t *this, pen_t vendor_id) +{ + enumerator_t *enumerator; + vendor_entry_t *entry; + enum_name_t *names = NULL; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == vendor_id) + { + names = entry->comp_func_names; + break; + } + } + enumerator->destroy(enumerator); + + return names; +} + +METHOD(pts_component_manager_t, get_qualifier_type_names, enum_name_t*, + private_pts_component_manager_t *this, pen_t vendor_id) +{ + enumerator_t *enumerator; + vendor_entry_t *entry; + enum_name_t *names = NULL; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == vendor_id) + { + names = entry->qualifier_type_names; + break; + } + } + enumerator->destroy(enumerator); + + return names; +} + +METHOD(pts_component_manager_t, add_component, void, + private_pts_component_manager_t *this, pen_t vendor_id, u_int32_t name, + pts_component_create_t create) +{ + enumerator_t *enumerator; + vendor_entry_t *entry; + component_entry_t *component; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == vendor_id) + { + component = malloc_thing(component_entry_t); + component->name = name; + component->create = create; + + entry->components->insert_last(entry->components, component); + DBG2(DBG_PTS, "added %N functional component '%N'", + pen_names, vendor_id, + get_comp_func_names(this, vendor_id), name); + } + } + enumerator->destroy(enumerator); +} + +METHOD(pts_component_manager_t, remove_vendor, void, + private_pts_component_manager_t *this, pen_t vendor_id) +{ + enumerator_t *enumerator; + vendor_entry_t *entry; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == vendor_id) + { + this->list->remove_at(this->list, enumerator); + vendor_entry_destroy(entry); + DBG2(DBG_PTS, "removed %N functional component namespace", + pen_names, vendor_id); + } + } + enumerator->destroy(enumerator); +} + +METHOD(pts_component_manager_t, get_qualifier, u_int8_t, + private_pts_component_manager_t *this, pts_comp_func_name_t *name, + char *flags) +{ + enumerator_t *enumerator; + vendor_entry_t *entry; + u_int8_t qualifier, size, flag, type = 0; + int i; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == name->get_vendor_id(name)) + { + qualifier = name->get_qualifier(name); + size = entry->qualifier_type_size; + + /* mask qualifier type field */ + type = qualifier & ((1 << size) - 1); + + /* determine flags */ + size = PTS_QUALIFIER_SIZE - size; + flag = (1 << (PTS_QUALIFIER_SIZE - 1)); + if (flags) + { + for (i = 0 ; i < size; i++) + { + flags[i] = (qualifier & flag) ? + entry->qualifier_flag_names[i] : '.'; + flag >>= 1; + } + flags[size] = '\0'; + } + } + } + enumerator->destroy(enumerator); + + return type; +} + +METHOD(pts_component_manager_t, create, pts_component_t*, + private_pts_component_manager_t *this, + pts_comp_func_name_t *name, u_int32_t depth, pts_database_t *pts_db) +{ + enumerator_t *enumerator, *e2; + vendor_entry_t *entry; + component_entry_t *entry2; + pts_component_t *component = NULL; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->vendor_id == name->get_vendor_id(name)) + { + e2 = entry->components->create_enumerator(entry->components); + while (e2->enumerate(e2, &entry2)) + { + if (entry2->name == name->get_name(name) && entry2->create) + { + component = entry2->create(name->get_qualifier(name), + depth, pts_db); + break; + } + } + e2->destroy(e2); + break; + } + } + enumerator->destroy(enumerator); + + return component; +} + +METHOD(pts_component_manager_t, destroy, void, + private_pts_component_manager_t *this) +{ + this->list->destroy_function(this->list, (void *)vendor_entry_destroy); + free(this); +} + +/** + * See header + */ +pts_component_manager_t *pts_component_manager_create(void) +{ + private_pts_component_manager_t *this; + + INIT(this, + .public = { + .add_vendor = _add_vendor, + .add_component = _add_component, + .remove_vendor = _remove_vendor, + .get_comp_func_names = _get_comp_func_names, + .get_qualifier_type_names = _get_qualifier_type_names, + .get_qualifier = _get_qualifier, + .create = _create, + .destroy = _destroy, + }, + .list = linked_list_create(), + ); + + return &this->public; +} + diff --git a/src/libpts/pts/components/pts_component_manager.h b/src/libpts/pts/components/pts_component_manager.h new file mode 100644 index 000000000..0079d0e26 --- /dev/null +++ b/src/libpts/pts/components/pts_component_manager.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2011 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 pts_component_manager pts_component_manager + * @{ @ingroup pts + */ + +#ifndef PTS_COMPONENT_MANAGER_H_ +#define PTS_COMPONENT_MANAGER_H_ + +typedef struct pts_component_manager_t pts_component_manager_t; + +#include "pts/pts_database.h" +#include "pts/components/pts_component.h" +#include "pts/components/pts_comp_func_name.h" + +#include <library.h> +#include <pen/pen.h> + +typedef pts_component_t* (*pts_component_create_t)(u_int8_t qualifier, + u_int32_t depth, + pts_database_t *pts_db); + +/** + * Manages PTS Functional Components + */ +struct pts_component_manager_t { + + /** + * Add vendor-specific functional component names + * + * @param vendor_id Private Enterprise Number (PEN) + * @param comp_func_names Vendor-specific Component Functional names + * @param qualifier_type_size Vendor-specific Qualifier Type size + * @param qualifier_flag_names Vendor-specific Qualifier Flag names + * @param qualifier_type_names Vendor-specific Qualifier Type names + */ + void (*add_vendor)(pts_component_manager_t *this, pen_t vendor_id, + enum_name_t *comp_func_names, + int qualifier_type_size, + char *qualifier_flag_names, + enum_name_t *qualifier_type_names); + + /** + * Add vendor-specific functional component + * + * @param vendor_id Private Enterprise Number (PEN) + * @param names Component Functional Name + * @param create Functional Component creation method + */ + void (*add_component)(pts_component_manager_t *this, pen_t vendor_id, + u_int32_t name, pts_component_create_t create); + + /** + * Remove vendor-specific components and associated namespace + * + * @param vendor_id Private Enterprise Number (PEN) + */ + void (*remove_vendor)(pts_component_manager_t *this, pen_t vendor_id); + + /** + * Return the Functional Component names for a given vendor ID + * + * @param vendor_id Private Enterprise Number (PEN) + * @return Comp. Func. names if found, NULL else + */ + enum_name_t* (*get_comp_func_names)(pts_component_manager_t *this, + pen_t vendor_id); + + /** + * Return the Functional Component Qualifier Type names for a given vendor ID + * + * @param vendor_id Private Enterprise Number (PEN) + * @return Qualifier Type names if found, NULL else + */ + enum_name_t* (*get_qualifier_type_names)(pts_component_manager_t *this, + pen_t vendor_id); + + /** + * Return the Qualifier Type and Flags + * + * @param name Component Functional Name + * @param flags Qualifier Flags as a string in a char buffer + * @return Qualifier Type + */ + u_int8_t (*get_qualifier)(pts_component_manager_t *this, + pts_comp_func_name_t *name, char *flags); + + /** + * Create a PTS Component object from a Functional Component Name object + * + * @param name Component Functional Name + * @param depth Sub-component Depth + * @param pts_db PTS measurement database + * @return Component object if supported, NULL else + */ + pts_component_t* (*create)(pts_component_manager_t *this, + pts_comp_func_name_t *name, u_int32_t depth, + pts_database_t *pts_db); + + /** + * Destroys a pts_component_manager_t object. + */ + void (*destroy)(pts_component_manager_t *this); +}; + +/** + * Create a PA-TNC attribute manager + */ +pts_component_manager_t* pts_component_manager_create(void); + +#endif /** PTS_COMPONENT_MANAGER_H_ @}*/ diff --git a/src/libpts/pts/components/tcg/tcg_comp_func_name.c b/src/libpts/pts/components/tcg/tcg_comp_func_name.c new file mode 100644 index 000000000..a70c84e48 --- /dev/null +++ b/src/libpts/pts/components/tcg/tcg_comp_func_name.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 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 "tcg_comp_func_name.h" + +char pts_tcg_qualifier_flag_names[] = { 'K', 'S' }; + +ENUM_BEGIN(pts_tcg_qualifier_type_names, PTS_TCG_QUALIFIER_TYPE_UNKNOWN, + PTS_TCG_QUALIFIER_TYPE_TNC, + "Unknown", + "Trusted Platform", + "Operating System", + "Graphical User Interface", + "Application", + "Networking", + "Library", + "TNC Defined Component" +); +ENUM_NEXT(pts_tcg_qualifier_type_names, PTS_TCG_QUALIFIER_TYPE_ALL, + PTS_TCG_QUALIFIER_TYPE_ALL, + PTS_TCG_QUALIFIER_TYPE_TNC, + "All Matching Components" +); +ENUM_END(pts_tcg_qualifier_type_names, PTS_TCG_QUALIFIER_TYPE_ALL); + +ENUM(pts_tcg_comp_func_names, PTS_TCG_COMP_FUNC_NAME_IGNORE, + PTS_TCG_COMP_FUNC_NAME_OPT_ROMS, + "Ignore", + "CRTM", + "BIOS", + "Platform Extensions", + "Motherboard Firmware", + "Initial Program Loader", + "Option ROMs" +); + diff --git a/src/libpts/pts/components/tcg/tcg_comp_func_name.h b/src/libpts/pts/components/tcg/tcg_comp_func_name.h new file mode 100644 index 000000000..9708ad09d --- /dev/null +++ b/src/libpts/pts/components/tcg/tcg_comp_func_name.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_tcg_comp_func_name pts_tcg_comp_func_name + * @{ @ingroup pts + */ + +#ifndef PTS_TCG_COMP_FUNC_NAME_H_ +#define PTS_TCG_COMP_FUNC_NAME_H_ + +typedef enum pts_tcg_qualifier_type_t pts_tcg_qualifier_type_t; +typedef enum pts_tcg_comp_func_name_t pts_tcp_comp_func_name_t; + +#include <library.h> + +/** + * PTS Component Functional Name Qualifier Flags for the TCG namespace + * see section 5.2 of PTS Protocol: Binding to TNC IF-M Specification + * + * 0 1 2 3 4 5 + * +-+-+-+-+-+-+ + * |K|S| Type | + * +-+-+-+-+-+-+ + */ +#define PTS_TCG_QUALIFIER_FLAG_KERNEL (1<<5) +#define PTS_TCG_QUALIFIER_FLAG_SUB (1<<4) + +extern char pts_tcg_qualifier_flag_names[]; + +/** + * Size of the PTS Component Functional Name Qualifier Type field + */ +#define PTS_TCG_QUALIFIER_TYPE_SIZE 4 + +/** + * PTS Component Functional Name Qualifier Types for the TCG namespace + * see section 5.2 of PTS Protocol: Binding to TNC IF-M Specification + */ +enum pts_tcg_qualifier_type_t { + /** Unknown */ + PTS_TCG_QUALIFIER_TYPE_UNKNOWN = 0x0, + /** Trusted Platform */ + PTS_TCG_QUALIFIER_TYPE_TRUSTED = 0x1, + /** Operating System */ + PTS_TCG_QUALIFIER_TYPE_OS = 0x2, + /** Graphical User Interface */ + PTS_TCG_QUALIFIER_TYPE_GUI = 0x3, + /** Application */ + PTS_TCG_QUALIFIER_TYPE_APP = 0x4, + /** Networking */ + PTS_TCG_QUALIFIER_TYPE_NET = 0x5, + /** Library */ + PTS_TCG_QUALIFIER_TYPE_LIB = 0x6, + /** TNC Defined Component */ + PTS_TCG_QUALIFIER_TYPE_TNC = 0x7, + /** All matching Components */ + PTS_TCG_QUALIFIER_TYPE_ALL = 0xF, +}; + +extern enum_name_t *pts_tcg_qualifier_type_names; + +/** + * PTS Component Functional Name Binary Enumeration for the TCG namespace + * see section 5.3 of PTS Protocol: Binding to TNC IF-M Specification + */ +enum pts_tcg_comp_func_name_t { + /** Ignore */ + PTS_TCG_COMP_FUNC_NAME_IGNORE = 0x0000, + /** CRTM */ + PTS_TCG_COMP_FUNC_NAME_CRTM = 0x0001, + /** BIOS */ + PTS_TCG_COMP_FUNC_NAME_BIOS = 0x0002, + /** Platform Extensions */ + PTS_TCG_COMP_FUNC_NAME_PLATFORM_EXT = 0x0003, + /** Motherboard Firmware */ + PTS_TCG_COMP_FUNC_NAME_BOARD = 0x0004, + /** Initial Program Loader */ + PTS_TCG_COMP_FUNC_NAME_INIT_LOADER = 0x0005, + /** Option ROMs */ + PTS_TCG_COMP_FUNC_NAME_OPT_ROMS = 0x0006, +}; + +extern enum_name_t *pts_tcg_comp_func_names; + +#endif /** PTS_TCG_COMP_FUNC_NAME_H_ @}*/ diff --git a/src/libpts/pts/pts.c b/src/libpts/pts/pts.c index f768b4679..65ae2b2d2 100644 --- a/src/libpts/pts/pts.c +++ b/src/libpts/pts/pts.c @@ -17,6 +17,8 @@ #include <debug.h> #include <crypto/hashers/hasher.h> +#include <bio/bio_writer.h> +#include <bio/bio_reader.h> #include <trousers/tss.h> #include <trousers/trousers.h> @@ -27,6 +29,16 @@ #define PTS_BUF_SIZE 4096 +/** + * Maximum number of PCR's of TPM, TPM Spec 1.2 + */ +#define PCR_MAX_NUM 24 + +/** + * Number of bytes that can be saved in a PCR of TPM, TPM Spec 1.2 + */ +#define PCR_LEN 20 + typedef struct private_pts_t private_pts_t; /** @@ -51,11 +63,41 @@ struct private_pts_t { pts_meas_algorithms_t algorithm; /** + * DH Hash Algorithm + */ + pts_meas_algorithms_t dh_hash_algorithm; + + /** + * PTS Diffie-Hellman Secret + */ + diffie_hellman_t *dh; + + /** + * PTS Diffie-Hellman Initiator Nonce + */ + chunk_t initiator_nonce; + + /** + * PTS Diffie-Hellman Responder Nonce + */ + chunk_t responder_nonce; + + /** + * Secret assessment value to be used for TPM Quote as an external data + */ + chunk_t secret; + + /** * Platform and OS Info */ char *platform_info; /** + * TRUE if IMC-PTS, FALSE if IMV-PTS + */ + bool is_imc; + + /** * Do we have an activated TPM */ bool has_tpm; @@ -66,10 +108,40 @@ struct private_pts_t { chunk_t tpm_version_info; /** + * Contains TSS Blob structure for AIK + */ + chunk_t aik_blob; + + /** * Contains a Attestation Identity Key or Certificate */ certificate_t *aik; + /** + * Table of extended PCRs with corresponding values + */ + u_char* pcrs[PCR_MAX_NUM]; + + /** + * Length of PCR registers + */ + size_t pcr_len; + + /** + * Number of extended PCR registers + */ + u_int32_t pcr_count; + + /** + * Highest extended PCR register + */ + u_int32_t pcr_max; + + /** + * Bitmap of extended PCR registers + */ + u_int8_t pcr_select[PCR_MAX_NUM / 8]; + }; METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t, @@ -79,7 +151,7 @@ METHOD(pts_t, get_proto_caps, pts_proto_caps_flag_t, } METHOD(pts_t, set_proto_caps, void, - private_pts_t *this, pts_proto_caps_flag_t flags) + private_pts_t *this, pts_proto_caps_flag_t flags) { this->proto_caps = flags; DBG2(DBG_PTS, "supported PTS protocol capabilities: %s%s%s%s%s", @@ -91,25 +163,143 @@ METHOD(pts_t, set_proto_caps, void, } METHOD(pts_t, get_meas_algorithm, pts_meas_algorithms_t, - private_pts_t *this) + private_pts_t *this) { return this->algorithm; } METHOD(pts_t, set_meas_algorithm, void, - private_pts_t *this, pts_meas_algorithms_t algorithm) + private_pts_t *this, pts_meas_algorithms_t algorithm) { hash_algorithm_t hash_alg; - hash_alg = pts_meas_to_hash_algorithm(algorithm); + hash_alg = pts_meas_algo_to_hash(algorithm); DBG2(DBG_PTS, "selected PTS measurement algorithm is %N", - hash_algorithm_names, hash_alg); + hash_algorithm_names, hash_alg); if (hash_alg != HASH_UNKNOWN) { this->algorithm = algorithm; } } +METHOD(pts_t, get_dh_hash_algorithm, pts_meas_algorithms_t, + private_pts_t *this) +{ + return this->dh_hash_algorithm; +} + +METHOD(pts_t, set_dh_hash_algorithm, void, + private_pts_t *this, pts_meas_algorithms_t algorithm) +{ + hash_algorithm_t hash_alg; + + hash_alg = pts_meas_algo_to_hash(algorithm); + DBG2(DBG_PTS, "selected DH hash algorithm is %N", + hash_algorithm_names, hash_alg); + if (hash_alg != HASH_UNKNOWN) + { + this->dh_hash_algorithm = algorithm; + } +} + + +METHOD(pts_t, create_dh_nonce, bool, + private_pts_t *this, pts_dh_group_t group, int nonce_len) +{ + diffie_hellman_group_t dh_group; + chunk_t *nonce; + rng_t *rng; + + dh_group = pts_dh_group_to_ike(group); + DBG2(DBG_PTS, "selected PTS DH group is %N", + diffie_hellman_group_names, dh_group); + DESTROY_IF(this->dh); + this->dh = lib->crypto->create_dh(lib->crypto, dh_group); + + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (!rng) + { + DBG1(DBG_PTS, "no rng available"); + return FALSE; + } + DBG2(DBG_PTS, "nonce length is %d", nonce_len); + nonce = this->is_imc ? &this->responder_nonce : &this->initiator_nonce; + chunk_free(nonce); + rng->allocate_bytes(rng, nonce_len, nonce); + rng->destroy(rng); + + return TRUE; +} + +METHOD(pts_t, get_my_public_value, void, + private_pts_t *this, chunk_t *value, chunk_t *nonce) +{ + this->dh->get_my_public_value(this->dh, value); + *nonce = this->is_imc ? this->responder_nonce : this->initiator_nonce; +} + +METHOD(pts_t, set_peer_public_value, void, + private_pts_t *this, chunk_t value, chunk_t nonce) +{ + this->dh->set_other_public_value(this->dh, value); + + nonce = chunk_clone(nonce); + if (this->is_imc) + { + this->initiator_nonce = nonce; + } + else + { + this->responder_nonce = nonce; + } +} + +METHOD(pts_t, calculate_secret, bool, + private_pts_t *this) +{ + hasher_t *hasher; + hash_algorithm_t hash_alg; + chunk_t shared_secret; + + /* Check presence of nonces */ + if (!this->initiator_nonce.len || !this->responder_nonce.len) + { + DBG1(DBG_PTS, "initiator and/or responder nonce is not available"); + return FALSE; + } + DBG3(DBG_PTS, "initiator nonce: %B", &this->initiator_nonce); + DBG3(DBG_PTS, "responder nonce: %B", &this->responder_nonce); + + /* Calculate the DH secret */ + if (this->dh->get_shared_secret(this->dh, &shared_secret) != SUCCESS) + { + DBG1(DBG_PTS, "shared DH secret computation failed"); + return FALSE; + } + DBG3(DBG_PTS, "shared DH secret: %B", &shared_secret); + + /* Calculate the secret assessment value */ + hash_alg = pts_meas_algo_to_hash(this->dh_hash_algorithm); + hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); + + hasher->allocate_hash(hasher, chunk_from_chars('1'), NULL); + hasher->allocate_hash(hasher, this->initiator_nonce, NULL); + hasher->allocate_hash(hasher, this->responder_nonce, NULL); + hasher->allocate_hash(hasher, shared_secret, &this->secret); + hasher->destroy(hasher); + + /* The DH secret must be destroyed */ + chunk_clear(&shared_secret); + + /* + * Truncate the hash to 20 bytes to fit the ExternalData + * argument of the TPM Quote command + */ + this->secret.len = min(this->secret.len, 20); + DBG3(DBG_PTS, "secret assessment value: %B", &this->secret); + return TRUE; +} + /** * Print TPM 1.2 Version Info */ @@ -138,20 +328,20 @@ static void print_tpm_version_info(private_pts_t *this) } METHOD(pts_t, get_platform_info, char*, - private_pts_t *this) + private_pts_t *this) { return this->platform_info; } METHOD(pts_t, set_platform_info, void, - private_pts_t *this, char *info) + private_pts_t *this, char *info) { free(this->platform_info); this->platform_info = strdup(info); } METHOD(pts_t, get_tpm_version_info, bool, - private_pts_t *this, chunk_t *info) + private_pts_t *this, chunk_t *info) { if (!this->has_tpm) { @@ -163,14 +353,62 @@ METHOD(pts_t, get_tpm_version_info, bool, } METHOD(pts_t, set_tpm_version_info, void, - private_pts_t *this, chunk_t info) + private_pts_t *this, chunk_t info) { this->tpm_version_info = chunk_clone(info); print_tpm_version_info(this); } +METHOD(pts_t, get_pcr_len, size_t, + private_pts_t *this) +{ + return this->pcr_len; +} + +/** + * Load an AIK Blob (TSS_TSPATTRIB_KEYBLOB_BLOB attribute) + */ +static void load_aik_blob(private_pts_t *this) +{ + char *blob_path; + FILE *fp; + u_int32_t aikBlobLen; + + blob_path = lib->settings->get_str(lib->settings, + "libimcv.plugins.imc-attestation.aik_blob", NULL); + + if (blob_path) + { + /* Read aik key blob from a file */ + if ((fp = fopen(blob_path, "r")) == NULL) + { + DBG1(DBG_PTS, "unable to open AIK Blob file: %s", blob_path); + return; + } + + fseek(fp, 0, SEEK_END); + aikBlobLen = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + this->aik_blob = chunk_alloc(aikBlobLen); + if (fread(this->aik_blob.ptr, 1, aikBlobLen, fp)) + { + DBG2(DBG_PTS, "loaded AIK Blob from '%s'", blob_path); + DBG3(DBG_PTS, "AIK Blob: %B", &this->aik_blob); + } + else + { + DBG1(DBG_PTS, "unable to read AIK Blob file '%s'", blob_path); + } + fclose(fp); + return; + } + + DBG1(DBG_PTS, "AIK Blob is not available"); +} + /** - * Load an AIK certificate or public key, + * Load an AIK certificate or public key * the certificate having precedence over the public key if both are present */ static void load_aik(private_pts_t *this) @@ -204,26 +442,52 @@ static void load_aik(private_pts_t *this) return; } } + DBG1(DBG_PTS, "neither AIK certificate nor public key is available"); } METHOD(pts_t, get_aik, certificate_t*, - private_pts_t *this) + private_pts_t *this) { - return this->aik; + return this->aik; } METHOD(pts_t, set_aik, void, - private_pts_t *this, certificate_t *aik) + private_pts_t *this, certificate_t *aik) { DESTROY_IF(this->aik); this->aik = aik->get_ref(aik); } -/** - * Compute a hash over a file - */ -static bool hash_file(hasher_t *hasher, char *pathname, u_char *hash) +METHOD(pts_t, get_aik_keyid, bool, + private_pts_t *this, chunk_t *keyid) +{ + public_key_t *public; + bool success; + + if (!this->aik) + { + DBG1(DBG_PTS, "no AIK certificate available"); + return FALSE; + } + public = this->aik->get_public_key(this->aik); + if (!public) + { + DBG1(DBG_PTS, "no AIK public key available"); + return FALSE; + } + success = public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, keyid); + if (!success) + { + DBG1(DBG_PTS, "no SHA-1 AIK public key info ID available"); + } + public->destroy(public); + + return success; +} + +METHOD(pts_t, hash_file, bool, + private_pts_t *this, hasher_t *hasher, char *pathname, u_char *hash) { u_char buffer[PTS_BUF_SIZE]; FILE *file; @@ -270,24 +534,23 @@ static char* get_filename(char *pathname) return filename; } -METHOD(pts_t, is_path_valid, bool, private_pts_t *this, char *path, - pts_error_code_t *error_code) +METHOD(pts_t, is_path_valid, bool, + private_pts_t *this, char *path, pts_error_code_t *error_code) { - int error; - struct stat sb; - + struct stat st; + *error_code = 0; - error = stat(path, &sb); - if (error == 0) + + if (!stat(path, &st)) { return TRUE; } - else if (error == ENOENT || error == ENOTDIR) + else if (errno == ENOENT || errno == ENOTDIR) { DBG1(DBG_PTS, "file/directory does not exist %s", path); *error_code = TCG_PTS_FILE_NOT_FOUND; } - else if (error == EFAULT) + else if (errno == EFAULT) { DBG1(DBG_PTS, "bad address %s", path); *error_code = TCG_PTS_INVALID_PATH; @@ -295,7 +558,7 @@ METHOD(pts_t, is_path_valid, bool, private_pts_t *this, char *path, else { DBG1(DBG_PTS, "error: %s occurred while validating path: %s", - strerror(error), path); + strerror(errno), path); return FALSE; } @@ -303,7 +566,7 @@ METHOD(pts_t, is_path_valid, bool, private_pts_t *this, char *path, } METHOD(pts_t, do_measurements, pts_file_meas_t*, - private_pts_t *this, u_int16_t request_id, char *pathname, bool is_directory) + private_pts_t *this, u_int16_t request_id, char *pathname, bool is_directory) { hasher_t *hasher; hash_algorithm_t hash_alg; @@ -312,11 +575,11 @@ METHOD(pts_t, do_measurements, pts_file_meas_t*, pts_file_meas_t *measurements; /* Create a hasher */ - hash_alg = pts_meas_to_hash_algorithm(this->algorithm); + hash_alg = pts_meas_algo_to_hash(this->algorithm); hasher = lib->crypto->create_hasher(lib->crypto, hash_alg); if (!hasher) { - DBG1(DBG_PTS, " hasher %N not available", hash_algorithm_names, hash_alg); + DBG1(DBG_PTS, "hasher %N not available", hash_algorithm_names, hash_alg); return NULL; } @@ -346,7 +609,7 @@ METHOD(pts_t, do_measurements, pts_file_meas_t*, /* measure regular files only */ if (S_ISREG(st.st_mode) && *rel_name != '.') { - if (!hash_file(hasher, abs_name, hash)) + if (!hash_file(this, hasher, abs_name, hash)) { enumerator->destroy(enumerator); hasher->destroy(hasher); @@ -363,7 +626,7 @@ METHOD(pts_t, do_measurements, pts_file_meas_t*, { char *filename; - if (!hash_file(hasher, pathname, hash)) + if (!hash_file(this, hasher, pathname, hash)) { hasher->destroy(hasher); measurements->destroy(measurements); @@ -378,23 +641,683 @@ METHOD(pts_t, do_measurements, pts_file_meas_t*, return measurements; } +/** + * Obtain statistical information describing a file + */ +static bool file_metadata(char *pathname, pts_file_metadata_t **entry) +{ + struct stat st; + pts_file_metadata_t *this; + + this = malloc_thing(pts_file_metadata_t); + + if (stat(pathname, &st)) + { + DBG1(DBG_PTS, "unable to obtain statistics about '%s'", pathname); + return FALSE; + } + + if (S_ISREG(st.st_mode)) + { + this->type = PTS_FILE_REGULAR; + } + else if (S_ISDIR(st.st_mode)) + { + this->type = PTS_FILE_DIRECTORY; + } + else if (S_ISCHR(st.st_mode)) + { + this->type = PTS_FILE_CHAR_SPEC; + } + else if (S_ISBLK(st.st_mode)) + { + this->type = PTS_FILE_BLOCK_SPEC; + } + else if (S_ISFIFO(st.st_mode)) + { + this->type = PTS_FILE_FIFO; + } + else if (S_ISLNK(st.st_mode)) + { + this->type = PTS_FILE_SYM_LINK; + } + else if (S_ISSOCK(st.st_mode)) + { + this->type = PTS_FILE_SOCKET; + } + else + { + this->type = PTS_FILE_OTHER; + } + + this->filesize = st.st_size; + this->created = st.st_ctime; + this->modified = st.st_mtime; + this->accessed = st.st_atime; + this->owner = st.st_uid; + this->group = st.st_gid; + + *entry = this; + return TRUE; +} + +METHOD(pts_t, get_metadata, pts_file_meta_t*, + private_pts_t *this, char *pathname, bool is_directory) +{ + pts_file_meta_t *metadata; + pts_file_metadata_t *entry; + + /* Create a metadata object */ + metadata = pts_file_meta_create(); + + if (is_directory) + { + enumerator_t *enumerator; + char *rel_name, *abs_name; + struct stat st; + + enumerator = enumerator_create_directory(pathname); + if (!enumerator) + { + DBG1(DBG_PTS," directory '%s' can not be opened, %s", pathname, + strerror(errno)); + metadata->destroy(metadata); + return NULL; + } + while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st)) + { + /* measure regular files only */ + if (S_ISREG(st.st_mode) && *rel_name != '.') + { + if (!file_metadata(abs_name, &entry)) + { + enumerator->destroy(enumerator); + metadata->destroy(metadata); + return NULL; + } + entry->filename = strdup(rel_name); + metadata->add(metadata, entry); + } + } + enumerator->destroy(enumerator); + } + else + { + if (!file_metadata(pathname, &entry)) + { + metadata->destroy(metadata); + return NULL; + } + entry->filename = strdup(get_filename(pathname)); + metadata->add(metadata, entry); + } + + return metadata; +} + +METHOD(pts_t, read_pcr, bool, + private_pts_t *this, u_int32_t pcr_num, chunk_t *pcr_value) +{ + TSS_HCONTEXT hContext; + TSS_HTPM hTPM; + TSS_RESULT result; + chunk_t rgbPcrValue; + + bool success = FALSE; + + result = Tspi_Context_Create(&hContext); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result); + return FALSE; + } + + result = Tspi_Context_Connect(hContext, NULL); + if (result != TSS_SUCCESS) + { + goto err; + } + result = Tspi_Context_GetTpmObject (hContext, &hTPM); + if (result != TSS_SUCCESS) + { + goto err; + } + result = Tspi_TPM_PcrRead(hTPM, pcr_num, (UINT32*)&rgbPcrValue.len, &rgbPcrValue.ptr); + if (result != TSS_SUCCESS) + { + goto err; + } + *pcr_value = chunk_clone(rgbPcrValue); + DBG3(DBG_PTS, "PCR %d value:%B", pcr_num, pcr_value); + success = TRUE; + +err: + if (!success) + { + DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); + } + Tspi_Context_FreeMemory(hContext, NULL); + Tspi_Context_Close(hContext); + + return success; +} + +METHOD(pts_t, extend_pcr, bool, + private_pts_t *this, u_int32_t pcr_num, chunk_t input, chunk_t *output) +{ + TSS_HCONTEXT hContext; + TSS_HTPM hTPM; + TSS_RESULT result; + u_int32_t pcr_length; + chunk_t pcr_value; + + result = Tspi_Context_Create(&hContext); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", + result); + return FALSE; + } + result = Tspi_Context_Connect(hContext, NULL); + if (result != TSS_SUCCESS) + { + goto err; + } + result = Tspi_Context_GetTpmObject (hContext, &hTPM); + if (result != TSS_SUCCESS) + { + goto err; + } + + pcr_value = chunk_alloc(PCR_LEN); + result = Tspi_TPM_PcrExtend(hTPM, pcr_num, PCR_LEN, input.ptr, + NULL, &pcr_length, &pcr_value.ptr); + if (result != TSS_SUCCESS) + { + goto err; + } + + *output = pcr_value; + *output = chunk_clone(*output); + + DBG3(DBG_PTS, "PCR %d extended with: %B", pcr_num, &input); + DBG3(DBG_PTS, "PCR %d value after extend: %B", pcr_num, output); + + chunk_clear(&pcr_value); + Tspi_Context_FreeMemory(hContext, NULL); + Tspi_Context_Close(hContext); + + return TRUE; + +err: + DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); + + chunk_clear(&pcr_value); + Tspi_Context_FreeMemory(hContext, NULL); + Tspi_Context_Close(hContext); + + return FALSE; +} + + +static void clear_pcrs(private_pts_t *this) +{ + int i; + + for (i = 0; i <= this->pcr_max; i++) + { + free(this->pcrs[i]); + this->pcrs[i] = NULL; + } + this->pcr_count = 0; + this->pcr_max = 0; + + memset(this->pcr_select, 0x00, sizeof(this->pcr_select)); +} + +METHOD(pts_t, quote_tpm, bool, + private_pts_t *this, bool use_quote2, chunk_t *pcr_comp, chunk_t *quote_sig) +{ + TSS_HCONTEXT hContext; + TSS_HTPM hTPM; + TSS_HKEY hAIK; + TSS_HKEY hSRK; + TSS_HPOLICY srkUsagePolicy; + TSS_UUID SRK_UUID = TSS_UUID_SRK; + BYTE secret[] = TSS_WELL_KNOWN_SECRET; + TSS_HPCRS hPcrComposite; + TSS_VALIDATION valData; + TSS_RESULT result; + chunk_t quote_info; + BYTE* versionInfo; + u_int32_t versionInfoSize, pcr, i = 0, f = 1; + bool success = FALSE; + + result = Tspi_Context_Create(&hContext); + if (result != TSS_SUCCESS) + { + DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", + result); + return FALSE; + } + result = Tspi_Context_Connect(hContext, NULL); + if (result != TSS_SUCCESS) + { + goto err1; + } + result = Tspi_Context_GetTpmObject (hContext, &hTPM); + if (result != TSS_SUCCESS) + { + goto err1; + } + + /* Retrieve SRK from TPM and set the authentication to well known secret*/ + result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM, + SRK_UUID, &hSRK); + if (result != TSS_SUCCESS) + { + goto err1; + } + + result = Tspi_GetPolicyObject(hSRK, TSS_POLICY_USAGE, &srkUsagePolicy); + if (result != TSS_SUCCESS) + { + goto err1; + } + result = Tspi_Policy_SetSecret(srkUsagePolicy, TSS_SECRET_MODE_SHA1, + 20, secret); + if (result != TSS_SUCCESS) + { + goto err1; + } + + result = Tspi_Context_LoadKeyByBlob (hContext, hSRK, this->aik_blob.len, + this->aik_blob.ptr, &hAIK); + if (result != TSS_SUCCESS) + { + goto err1; + } + + /* Create PCR composite object */ + result = use_quote2 ? + Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, + TSS_PCRS_STRUCT_INFO_SHORT, &hPcrComposite) : + Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, + 0, &hPcrComposite); + if (result != TSS_SUCCESS) + { + goto err2; + } + + /* Select PCRs */ + for (pcr = 0; pcr <= this->pcr_max ; pcr++) + { + if (f == 256) + { + i++; + f = 1; + } + if (this->pcr_select[i] & f) + { + result = use_quote2 ? + Tspi_PcrComposite_SelectPcrIndexEx(hPcrComposite, pcr, + TSS_PCRS_DIRECTION_RELEASE) : + Tspi_PcrComposite_SelectPcrIndex(hPcrComposite, pcr); + if (result != TSS_SUCCESS) + { + goto err3; + } + } + f <<= 1; + } + + /* Set the Validation Data */ + valData.ulExternalDataLength = this->secret.len; + valData.rgbExternalData = (BYTE *)this->secret.ptr; + + + /* TPM Quote */ + result = use_quote2 ? + Tspi_TPM_Quote2(hTPM, hAIK, FALSE, hPcrComposite, &valData, + &versionInfoSize, &versionInfo): + Tspi_TPM_Quote(hTPM, hAIK, hPcrComposite, &valData); + if (result != TSS_SUCCESS) + { + goto err4; + } + + /* Set output chunks */ + *pcr_comp = chunk_alloc(HASH_SIZE_SHA1); + + if (use_quote2) + { + /* TPM_Composite_Hash is last 20 bytes of TPM_Quote_Info2 structure */ + memcpy(pcr_comp->ptr, valData.rgbData + valData.ulDataLength - HASH_SIZE_SHA1, + HASH_SIZE_SHA1); + } + else + { + /* TPM_Composite_Hash is 8-28th bytes of TPM_Quote_Info structure */ + memcpy(pcr_comp->ptr, valData.rgbData + 8, HASH_SIZE_SHA1); + } + DBG3(DBG_PTS, "Hash of PCR Composite: %#B", pcr_comp); + + quote_info = chunk_create(valData.rgbData, valData.ulDataLength); + DBG3(DBG_PTS, "TPM Quote Info: %B","e_info); + + *quote_sig = chunk_clone(chunk_create(valData.rgbValidationData, + valData.ulValidationDataLength)); + DBG3(DBG_PTS, "TPM Quote Signature: %B",quote_sig); + + success = TRUE; + + /* Cleanup */ +err4: + Tspi_Context_FreeMemory(hContext, NULL); + +err3: + Tspi_Context_CloseObject(hContext, hPcrComposite); + +err2: + Tspi_Context_CloseObject(hContext, hAIK); + +err1: + Tspi_Context_Close(hContext); + + if (!success) + { + DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); + } + clear_pcrs(this); + + return success; +} + +METHOD(pts_t, select_pcr, bool, + private_pts_t *this, u_int32_t pcr) +{ + u_int32_t i, f; + + if (pcr >= PCR_MAX_NUM) + { + DBG1(DBG_PTS, "PCR %u: number is larger than maximum of %u", + pcr, PCR_MAX_NUM-1); + return FALSE; + } + + /* Determine PCR selection flag */ + i = pcr / 8; + f = 1 << (pcr - 8*i); + + /* Has this PCR already been selected? */ + if (!(this->pcr_select[i] & f)) + { + this->pcr_select[i] |= f; + this->pcr_max = max(this->pcr_max, pcr); + this->pcr_count++; + } + + return TRUE; +} + +METHOD(pts_t, add_pcr, bool, + private_pts_t *this, u_int32_t pcr, chunk_t pcr_before, chunk_t pcr_after) +{ + if (pcr >= PCR_MAX_NUM) + { + DBG1(DBG_PTS, "PCR %u: number is larger than maximum of %u", + pcr, PCR_MAX_NUM-1); + return FALSE; + } + + /* Is the length of the PCR registers already set? */ + if (this->pcr_len) + { + if (pcr_after.len != this->pcr_len) + { + DBG1(DBG_PTS, "PCR %02u: length is %d bytes but should be %d bytes", + pcr_after.len, this->pcr_len); + return FALSE; + } + } + else + { + this->pcr_len = pcr_after.len; + } + + /* Has the value of the PCR register already been assigned? */ + if (this->pcrs[pcr]) + { + if (!memeq(this->pcrs[pcr], pcr_before.ptr, this->pcr_len)) + { + DBG1(DBG_PTS, "PCR %02u: new pcr_before value does not equal " + "old pcr_after value"); + } + /* remove the old PCR value */ + free(this->pcrs[pcr]); + } + else + { + /* add extended PCR Register */ + this->pcr_select[pcr / 8] |= 1 << (pcr % 8); + this->pcr_max = max(this->pcr_max, pcr); + this->pcr_count++; + } + + /* Duplicate and store current PCR value */ + pcr_after = chunk_clone(pcr_after); + this->pcrs[pcr] = pcr_after.ptr; + + return TRUE; +} + +/** + * TPM_QUOTE_INFO structure: + * 4 bytes of version + * 4 bytes 'Q' 'U' 'O' 'T' + * 20 byte SHA1 of TCPA_PCR_COMPOSITE + * 20 byte nonce + * + * TPM_QUOTE_INFO2 structure: + * 2 bytes Tag 0x0036 TPM_Tag_Quote_info2 + * 4 bytes 'Q' 'U' 'T' '2' + * 20 bytes nonce + * 26 bytes PCR_INFO_SHORT + */ + +METHOD(pts_t, get_quote_info, bool, + private_pts_t *this, bool use_quote2, bool use_ver_info, + pts_meas_algorithms_t comp_hash_algo, + chunk_t *out_pcr_comp, chunk_t *out_quote_info) +{ + u_int8_t size_of_select; + int pcr_comp_len, i; + chunk_t pcr_comp, hash_pcr_comp; + bio_writer_t *writer; + hasher_t *hasher; + + if (this->pcr_count == 0) + { + DBG1(DBG_PTS, "No extended PCR entries available, " + "unable to construct TPM Quote Info"); + return FALSE; + } + if (!this->secret.ptr) + { + DBG1(DBG_PTS, "Secret assessment value unavailable, ", + "unable to construct TPM Quote Info"); + return FALSE; + } + if (use_quote2 && use_ver_info && !this->tpm_version_info.ptr) + { + DBG1(DBG_PTS, "TPM Version Information unavailable, ", + "unable to construct TPM Quote Info2"); + return FALSE; + } + + /** + * A TPM v1.2 has 24 PCR Registers + * so the bitmask field length used by TrouSerS is at least 3 bytes + */ + size_of_select = max(PCR_MAX_NUM / 8, 1 + this->pcr_max / 8); + pcr_comp_len = 2 + size_of_select + 4 + this->pcr_count * this->pcr_len; + + writer = bio_writer_create(pcr_comp_len); + + writer->write_uint16(writer, size_of_select); + for (i = 0; i < size_of_select; i++) + { + writer->write_uint8(writer, this->pcr_select[i]); + } + + writer->write_uint32(writer, this->pcr_count * this->pcr_len); + for (i = 0; i < 8 * size_of_select; i++) + { + if (this->pcrs[i]) + { + writer->write_data(writer, chunk_create(this->pcrs[i], this->pcr_len)); + } + } + pcr_comp = chunk_clone(writer->get_buf(writer)); + DBG3(DBG_PTS, "constructed PCR Composite: %B", &pcr_comp); + + writer->destroy(writer); + + /* Output the TPM_PCR_COMPOSITE expected from IMC */ + if (comp_hash_algo) + { + hash_algorithm_t algo; + + algo = pts_meas_algo_to_hash(comp_hash_algo); + hasher = lib->crypto->create_hasher(lib->crypto, algo); + + /* Hash the PCR Composite Structure */ + hasher->allocate_hash(hasher, pcr_comp, out_pcr_comp); + DBG3(DBG_PTS, "constructed PCR Composite hash: %#B", out_pcr_comp); + hasher->destroy(hasher); + } + else + { + *out_pcr_comp = chunk_clone(pcr_comp); + } + + /* SHA1 hash of PCR Composite to construct TPM_QUOTE_INFO */ + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + hasher->allocate_hash(hasher, pcr_comp, &hash_pcr_comp); + hasher->destroy(hasher); + + /* Construct TPM_QUOTE_INFO/TPM_QUOTE_INFO2 structure */ + writer = bio_writer_create(TPM_QUOTE_INFO_LEN); + + if (use_quote2) + { + /* TPM Structure Tag */ + writer->write_uint16(writer, TPM_TAG_QUOTE_INFO2); + + /* Magic QUT2 value */ + writer->write_data(writer, chunk_create("QUT2", 4)); + + /* Secret assessment value 20 bytes (nonce) */ + writer->write_data(writer, this->secret); + + /* Length of the PCR selection field */ + writer->write_uint16(writer, size_of_select); + + /* PCR selection */ + for (i = 0; i < size_of_select ; i++) + { + writer->write_uint8(writer, this->pcr_select[i]); + } + + /* TPM Locality Selection */ + writer->write_uint8(writer, TPM_LOC_ZERO); + + /* PCR Composite Hash */ + writer->write_data(writer, hash_pcr_comp); + + if (use_ver_info) + { + /* TPM version Info */ + writer->write_data(writer, this->tpm_version_info); + } + } + else + { + /* Version number */ + writer->write_data(writer, chunk_from_chars(1, 1, 0, 0)); + + /* Magic QUOT value */ + writer->write_data(writer, chunk_create("QUOT", 4)); + + /* PCR Composite Hash */ + writer->write_data(writer, hash_pcr_comp); + + /* Secret assessment value 20 bytes (nonce) */ + writer->write_data(writer, this->secret); + } + + /* TPM Quote Info */ + *out_quote_info = chunk_clone(writer->get_buf(writer)); + DBG3(DBG_PTS, "constructed TPM Quote Info: %B", out_quote_info); + + writer->destroy(writer); + free(pcr_comp.ptr); + free(hash_pcr_comp.ptr); + clear_pcrs(this); + + return TRUE; +} + +METHOD(pts_t, verify_quote_signature, bool, + private_pts_t *this, chunk_t data, chunk_t signature) +{ + public_key_t *aik_pub_key; + + aik_pub_key = this->aik->get_public_key(this->aik); + if (!aik_pub_key) + { + DBG1(DBG_PTS, "failed to get public key from AIK certificate"); + return FALSE; + } + + if (!aik_pub_key->verify(aik_pub_key, SIGN_RSA_EMSA_PKCS1_SHA1, + data, signature)) + { + DBG1(DBG_PTS, "signature verification failed for TPM Quote Info"); + DESTROY_IF(aik_pub_key); + return FALSE; + } + + aik_pub_key->destroy(aik_pub_key); + return TRUE; +} + METHOD(pts_t, destroy, void, - private_pts_t *this) + private_pts_t *this) { + clear_pcrs(this); DESTROY_IF(this->aik); + DESTROY_IF(this->dh); + free(this->initiator_nonce.ptr); + free(this->responder_nonce.ptr); + free(this->secret.ptr); free(this->platform_info); + free(this->aik_blob.ptr); free(this->tpm_version_info.ptr); free(this); } +#define RELEASE_LSB 0 +#define RELEASE_DEBIAN 1 + /** * Determine Linux distribution and hardware platform */ static char* extract_platform_info(void) { FILE *file; - char buf[BUF_LEN], *pos, *value = NULL; - int i, len; + char buf[BUF_LEN], *pos = buf, *value = NULL; + int i, len = BUF_LEN - 1; struct utsname uninfo; /* Linux/Unix distribution release info (from http://linuxmafia.com) */ @@ -420,6 +1343,7 @@ static char* extract_platform_info(void) }; const char description[] = "DISTRIB_DESCRIPTION=\""; + const char str_debian[] = "Debian "; for (i = 0; i < countof(releases); i++) { @@ -428,11 +1352,19 @@ static char* extract_platform_info(void) { continue; } + + if (i == RELEASE_DEBIAN) + { + strcpy(buf, str_debian); + pos += strlen(str_debian); + len -= strlen(str_debian); + } + fseek(file, 0, SEEK_END); - len = min(ftell(file), sizeof(buf)-1); + len = min(ftell(file), len); rewind(file); - buf[len] = '\0'; - if (fread(buf, 1, len, file) != len) + pos[len] = '\0'; + if (fread(pos, 1, len, file) != len) { DBG1(DBG_PTS, "failed to read file '%s'", releases[i]); fclose(file); @@ -440,7 +1372,7 @@ static char* extract_platform_info(void) } fclose(file); - if (i == 0) /* LSB release */ + if (i == RELEASE_LSB) { pos = strstr(buf, description); if (!pos) @@ -461,7 +1393,7 @@ static char* extract_platform_info(void) else { value = buf; - pos = strchr(value, '\n'); + pos = strchr(pos, '\n'); if (!pos) { DBG1(DBG_PTS, "failed to find end of release string"); @@ -504,7 +1436,8 @@ static bool has_tpm(private_pts_t *this) result = Tspi_Context_Create(&hContext); if (result != TSS_SUCCESS) { - DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", result); + DBG1(DBG_PTS, "TPM context could not be created: tss error 0x%x", + result); return FALSE; } result = Tspi_Context_Connect(hContext, NULL); @@ -526,10 +1459,14 @@ static bool has_tpm(private_pts_t *this) goto err; } this->tpm_version_info = chunk_clone(this->tpm_version_info); + + Tspi_Context_FreeMemory(hContext, NULL); + Tspi_Context_Close(hContext); return TRUE; err: DBG1(DBG_PTS, "TPM not available: tss error 0x%x", result); + Tspi_Context_FreeMemory(hContext, NULL); Tspi_Context_Close(hContext); return FALSE; } @@ -542,23 +1479,42 @@ pts_t *pts_create(bool is_imc) private_pts_t *this; INIT(this, - .public = { - .get_proto_caps = _get_proto_caps, - .set_proto_caps = _set_proto_caps, - .get_meas_algorithm = _get_meas_algorithm, - .set_meas_algorithm = _set_meas_algorithm, - .get_platform_info = _get_platform_info, - .set_platform_info = _set_platform_info, - .get_tpm_version_info = _get_tpm_version_info, - .set_tpm_version_info = _set_tpm_version_info, - .get_aik = _get_aik, - .set_aik = _set_aik, - .is_path_valid = _is_path_valid, - .do_measurements = _do_measurements, - .destroy = _destroy, - }, - .proto_caps = PTS_PROTO_CAPS_V, - .algorithm = PTS_MEAS_ALGO_SHA256, + .public = { + .get_proto_caps = _get_proto_caps, + .set_proto_caps = _set_proto_caps, + .get_meas_algorithm = _get_meas_algorithm, + .set_meas_algorithm = _set_meas_algorithm, + .get_dh_hash_algorithm = _get_dh_hash_algorithm, + .set_dh_hash_algorithm = _set_dh_hash_algorithm, + .create_dh_nonce = _create_dh_nonce, + .get_my_public_value = _get_my_public_value, + .set_peer_public_value = _set_peer_public_value, + .calculate_secret = _calculate_secret, + .get_platform_info = _get_platform_info, + .set_platform_info = _set_platform_info, + .get_tpm_version_info = _get_tpm_version_info, + .set_tpm_version_info = _set_tpm_version_info, + .get_pcr_len = _get_pcr_len, + .get_aik = _get_aik, + .set_aik = _set_aik, + .get_aik_keyid = _get_aik_keyid, + .is_path_valid = _is_path_valid, + .hash_file = _hash_file, + .do_measurements = _do_measurements, + .get_metadata = _get_metadata, + .read_pcr = _read_pcr, + .extend_pcr = _extend_pcr, + .quote_tpm = _quote_tpm, + .select_pcr = _select_pcr, + .add_pcr = _add_pcr, + .get_quote_info = _get_quote_info, + .verify_quote_signature = _verify_quote_signature, + .destroy = _destroy, + }, + .is_imc = is_imc, + .proto_caps = PTS_PROTO_CAPS_V, + .algorithm = PTS_MEAS_ALGO_SHA256, + .dh_hash_algorithm = PTS_MEAS_ALGO_SHA256, ); if (is_imc) @@ -568,15 +1524,16 @@ pts_t *pts_create(bool is_imc) if (has_tpm(this)) { this->has_tpm = TRUE; - this->proto_caps |= PTS_PROTO_CAPS_T; + this->pcr_len = PCR_LEN; + this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D; load_aik(this); + load_aik_blob(this); } } else { - this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_C; + this->proto_caps |= PTS_PROTO_CAPS_T | PTS_PROTO_CAPS_D; } return &this->public; } - diff --git a/src/libpts/pts/pts.h b/src/libpts/pts/pts.h index ef408c43f..212acb02a 100644 --- a/src/libpts/pts/pts.h +++ b/src/libpts/pts/pts.h @@ -27,17 +27,59 @@ typedef struct pts_t pts_t; #include "pts_proto_caps.h" #include "pts_meas_algo.h" #include "pts_file_meas.h" +#include "pts_file_meta.h" +#include "pts_dh_group.h" +#include "pts_req_func_comp_evid.h" +#include "pts_simple_evid_final.h" +#include "components/pts_comp_func_name.h" #include <library.h> +#include <utils/linked_list.h> /** * UTF-8 encoding of the character used to delimiter the filename */ -#define SOLIDUS_UTF 0x002F -#define REVERSE_SOLIDUS_UTF 0x005C +#define SOLIDUS_UTF 0x2F +#define REVERSE_SOLIDUS_UTF 0x5C /** - * Class implementing the TCG Platform Trust System (PTS) + * PCR indices used for measurements of various functional components + */ +#define PCR_BIOS 0 +#define PCR_PLATFORM_EXT 1 +#define PCR_MOTHERBOARD 1 +#define PCR_OPTION_ROMS 2 +#define PCR_IPL 4 + +#define PCR_TBOOT_POLICY 17 +#define PCR_TBOOT_MLE 18 + +#define PCR_TGRUB_MBR_STAGE1 4 +#define PCR_TGRUB_STAGE2_PART1 8 +#define PCR_TGRUB_STAGE2_PART2 9 +#define PCR_TGRUB_CMD_LINE_ARGS 12 +#define PCR_TGRUB_CHECKFILE 13 +#define PCR_TGRUB_LOADED_FILES 14 + +#define PCR_DEBUG 16 + +/** + * Length of the generated nonce used for calculation of shared secret + */ +#define ASSESSMENT_SECRET_LEN 20 + +/** + * Length of the TPM_QUOTE_INFO structure, TPM Spec 1.2 + */ +#define TPM_QUOTE_INFO_LEN 48 + +/** + * Hashing algorithm used by tboot and trustedGRUB + */ +#define TRUSTED_HASH_ALGO PTS_MEAS_ALGO_SHA1 + +/** + * Class implementing the TCG Platform Trust Service (PTS) * */ struct pts_t { @@ -45,96 +87,255 @@ struct pts_t { /** * Get PTS Protocol Capabilities * - * @return protocol capabilities flags + * @return Protocol capabilities flags */ pts_proto_caps_flag_t (*get_proto_caps)(pts_t *this); /** * Set PTS Protocol Capabilities * - * @param flags protocol capabilities flags + * @param flags Protocol capabilities flags */ void (*set_proto_caps)(pts_t *this, pts_proto_caps_flag_t flags); /** * Get PTS Measurement Algorithm * - * @return measurement algorithm + * @return PTS measurement algorithm */ pts_meas_algorithms_t (*get_meas_algorithm)(pts_t *this); /** * Set PTS Measurement Algorithm * - * @param algorithm measurement algorithm + * @param algorithm PTS measurement algorithm */ void (*set_meas_algorithm)(pts_t *this, pts_meas_algorithms_t algorithm); /** + * Get DH Hash Algorithm + * + * @return DH hash algorithm + */ + pts_meas_algorithms_t (*get_dh_hash_algorithm)(pts_t *this); + + /** + * Set DH Hash Algorithm + * + * @param algorithm DH hash algorithm + */ + void (*set_dh_hash_algorithm)(pts_t *this, pts_meas_algorithms_t algorithm); + + /** + * Create PTS Diffie-Hellman object and nonce + * + * @param group PTS DH group + * @param nonce_len Nonce length + * @return TRUE if creation was successful + * + */ + bool (*create_dh_nonce)(pts_t *this, pts_dh_group_t group, int nonce_len); + + /** + * Get my Diffie-Hellman public value + * + * @param value My public DH value + * @param nonce My DH nonce + */ + void (*get_my_public_value)(pts_t *this, chunk_t *value, chunk_t *nonce); + + /** + * Set peer Diffie.Hellman public value + * + * @param value Peer public DH value + * @param nonce Peer DH nonce + */ + void (*set_peer_public_value) (pts_t *this, chunk_t value, chunk_t nonce); + + /** + * Calculates assessment secret to be used for TPM Quote as ExternalData + * + * @return TRUE unless both DH public values + * and nonces are set + */ + bool (*calculate_secret) (pts_t *this); + + /** * Get Platform and OS Info * - * @return platform and OS info + * @return Platform and OS info */ char* (*get_platform_info)(pts_t *this); /** * Set Platform and OS Info * - * @param info platform and OS info + * @param info Platform and OS info */ void (*set_platform_info)(pts_t *this, char *info); /** * Get TPM 1.2 Version Info * - * @param info chunk containing a TPM_CAP_VERSION_INFO struct - * @return TRUE if TPM Version Info available + * @param info chunk containing a TPM_CAP_VERSION_INFO struct + * @return TRUE if TPM Version Info available */ bool (*get_tpm_version_info)(pts_t *this, chunk_t *info); /** * Set TPM 1.2 Version Info * - * @param info chunk containing a TPM_CAP_VERSION_INFO struct + * @param info chunk containing a TPM_CAP_VERSION_INFO struct */ void (*set_tpm_version_info)(pts_t *this, chunk_t info); - + + /** + * Get the length of the TPM PCR registers + * + * @return Length of PCR registers in bytes, 0 if undefined + */ + size_t (*get_pcr_len)(pts_t *this); + /** * Get Attestation Identity Certificate or Public Key * - * @return AIK Certificate or Public Key + * @return AIK Certificate or Public Key */ certificate_t* (*get_aik)(pts_t *this); - + /** * Set Attestation Identity Certificate or Public Key * - * @param aik AIK Certificate or Public Key + * @param aik AIK Certificate or Public Key */ void (*set_aik)(pts_t *this, certificate_t *aik); /** + * Get SHA-1 Attestation Identity Public Key Info ID + * + * @param keyid AIK ID + * @return TRUE if AIK ID exists + */ + bool (*get_aik_keyid)(pts_t *this, chunk_t *keyid); + + /** * Check whether path is valid file/directory on filesystem * - * @param path Absolute path - * @param error_code Output variable for PTS error code - * @return TRUE if path is valid or file/directory doesn't exist - * or path is invalid + * @param path Absolute path + * @param error_code Output variable for PTS error code + * @return TRUE if path is valid or file/directory + * doesn't exist or path is invalid * FALSE if local error occurred within stat function */ bool (*is_path_valid)(pts_t *this, char *path, pts_error_code_t *error_code); /** + * Compute a hash over a file + * @param hasher Hasher to be used + * @param pathname Absolute path of a file + * @param hash Buffer to keep hash output + * @return TRUE if path is valid and hashing succeeded + */ + bool (*hash_file)(pts_t *this, hasher_t *hasher, char *pathname, u_char *hash); + + /** * Do PTS File Measurements * - * @param request_id ID of PTS File Measurement Request - * @param pathname Absolute pathname of file to be measured - * @param is_directory if TRUE directory contents are measured - * @return PTS File Measurements of NULL if FAILED + * @param request_id ID of PTS File Measurement Request + * @param pathname Absolute pathname of file to be measured + * @param is_directory TRUE if directory contents are measured + * @return PTS File Measurements of NULL if FAILED */ pts_file_meas_t* (*do_measurements)(pts_t *this, u_int16_t request_id, char *pathname, bool is_directory); - + + /** + * Obtain file metadata + * + * @param pathname Absolute pathname of file/directory + * @param is_directory TRUE if directory contents are requested + * @return PTS File Metadata or NULL if FAILED + */ + pts_file_meta_t* (*get_metadata)(pts_t *this, char *pathname, + bool is_directory); + + /** + * Reads given PCR value and returns it + * Expects owner secret to be WELL_KNOWN_SECRET + * + * @param pcr_num Number of PCR to read + * @param pcr_value Chunk to save pcr read output + * @return NULL in case of TSS error, PCR value otherwise + */ + bool (*read_pcr)(pts_t *this, u_int32_t pcr_num, chunk_t *pcr_value); + + /** + * Extends given PCR with given value + * Expects owner secret to be WELL_KNOWN_SECRET + * + * @param pcr_num Number of PCR to extend + * @param input Value to extend + * @param output Chunk to save PCR value after extension + * @return FALSE in case of TSS error, TRUE otherwise + */ + bool (*extend_pcr)(pts_t *this, u_int32_t pcr_num, chunk_t input, + chunk_t *output); + + /** + * Quote over PCR's + * Expects owner and SRK secret to be WELL_KNOWN_SECRET and no password set for AIK + * + * @param use_quote2 Version of the Quote function to be used + * @param pcr_comp Chunk to save PCR composite structure + * @param quote_sig Chunk to save quote operation output + * without external data (anti-replay protection) + * @return FALSE in case of TSS error, TRUE otherwise + */ + bool (*quote_tpm)(pts_t *this, bool use_quote2, chunk_t *pcr_comp, + chunk_t *quote_sig); + + /** + * Mark an extended PCR as selected + * + * @param pcr Number of the extended PCR + * @return TRUE if PCR number is valid + */ + bool (*select_pcr)(pts_t *this, u_int32_t pcr); + + /** + * Add an extended PCR with its corresponding value + * + * @param pcr Number of the extended PCR + * @param pcr_before PCR value before extension + * @param pcr_after PCR value after extension + * @return TRUE if PCR number and register length is valid + */ + bool (*add_pcr)(pts_t *this, u_int32_t pcr, chunk_t pcr_before, + chunk_t pcr_after); + + /** + * Constructs and returns TPM Quote Info structure expected from IMC + * + * @param use_quote2 Version of the TPM_QUOTE_INFO to be constructed + * @param use_ver_info Version info is concatenated to TPM_QUOTE_INFO2 + * @param comp_hash_algo Composite Hash Algorithm + * @param pcr_comp Output variable to store PCR Composite + * @param quote_info Output variable to store TPM Quote Info + * @return FALSE in case of any error, TRUE otherwise + */ + bool (*get_quote_info)(pts_t *this, bool use_quote2, bool ver_info_included, + pts_meas_algorithms_t comp_hash_algo, + chunk_t *pcr_comp, chunk_t *quote_info); + + /** + * Constructs and returns PCR Quote Digest structure expected from IMC + * + * @param data Calculated TPM Quote Digest + * @param signature TPM Quote Signature received from IMC + * @return FALSE if signature is not verified + */ + bool (*verify_quote_signature)(pts_t *this, chunk_t data, chunk_t signature); + /** * Destroys a pts_t object. */ diff --git a/src/libpts/pts/pts_database.c b/src/libpts/pts/pts_database.c index 2706173ab..282755c0a 100644 --- a/src/libpts/pts/pts_database.c +++ b/src/libpts/pts/pts_database.c @@ -39,7 +39,7 @@ struct private_pts_database_t { }; -METHOD(pts_database_t, create_file_enumerator, enumerator_t*, +METHOD(pts_database_t, create_file_meas_enumerator, enumerator_t*, private_pts_database_t *this, char *product) { enumerator_t *e; @@ -49,12 +49,27 @@ METHOD(pts_database_t, create_file_enumerator, enumerator_t*, "SELECT f.id, f.type, f.path FROM files AS f " "JOIN product_file AS pf ON f.id = pf.file " "JOIN products AS p ON p.id = pf.product " - "WHERE p.name = ?", + "WHERE p.name = ? AND pf.measurement = 1", DB_TEXT, product, DB_INT, DB_INT, DB_TEXT); return e; } -METHOD(pts_database_t, create_hash_enumerator, enumerator_t*, +METHOD(pts_database_t, create_file_meta_enumerator, enumerator_t*, + private_pts_database_t *this, char *product) +{ + enumerator_t *e; + + /* look for all entries belonging to a product in the files table */ + e = this->db->query(this->db, + "SELECT f.type, f.path FROM files AS f " + "JOIN product_file AS pf ON f.id = pf.file " + "JOIN products AS p ON p.id = pf.product " + "WHERE p.name = ? AND pf.metadata = 1", + DB_TEXT, product, DB_INT, DB_TEXT); + return e; +} + +METHOD(pts_database_t, create_file_hash_enumerator, enumerator_t*, private_pts_database_t *this, char *product, pts_meas_algorithms_t algo, int id, bool is_dir) { @@ -82,6 +97,178 @@ METHOD(pts_database_t, create_hash_enumerator, enumerator_t*, return e; } +METHOD(pts_database_t, check_aik_keyid, status_t, + private_pts_database_t *this, chunk_t keyid, int *kid) +{ + enumerator_t *e; + + /* If the AIK is registered get the primary key */ + e = this->db->query(this->db, + "SELECT id FROM keys WHERE keyid = ?", DB_BLOB, keyid, DB_INT); + if (!e) + { + DBG1(DBG_PTS, "no database query enumerator returned"); + return FAILED; + } + if (!e->enumerate(e, kid)) + { + DBG1(DBG_PTS, "AIK %#B is not registered in database", &keyid); + e->destroy(e); + return FAILED; + } + e->destroy(e); + + return SUCCESS; +} + +METHOD(pts_database_t, create_comp_evid_enumerator, enumerator_t*, + private_pts_database_t *this, int kid) +{ + enumerator_t *e; + + /* look for all entries belonging to an AIK in the components table */ + e = this->db->query(this->db, + "SELECT c.vendor_id, c.name, c.qualifier, kc.depth " + "FROM components AS c " + "JOIN key_component AS kc ON c.id = kc.component " + "WHERE kc.key = ? ORDER BY kc.seq_no", + DB_INT, kid, DB_INT, DB_INT, DB_INT, DB_INT); + return e; +} + +METHOD(pts_database_t, check_comp_measurement, status_t, + private_pts_database_t *this, chunk_t measurement, int cid, int kid, + int seq_no, int pcr, pts_meas_algorithms_t algo) +{ + enumerator_t *e; + chunk_t hash; + status_t status = NOT_FOUND; + + e = this->db->query(this->db, + "SELECT hash FROM component_hashes " + "WHERE component = ? AND key = ? " + "AND seq_no = ? AND pcr = ? AND algo = ? ", + DB_INT, cid, DB_INT, kid, DB_INT, seq_no, + DB_INT, pcr, DB_INT, algo, DB_BLOB); + if (!e) + { + DBG1(DBG_PTS, "no database query enumerator returned"); + return FAILED; + } + + while (e->enumerate(e, &hash)) + { + if (chunk_equals(hash, measurement)) + { + status = SUCCESS; + break; + } + else + { + DBG1(DBG_PTS, "PCR %2d no matching component measurement #%d " + "found in database", pcr, seq_no); + DBG1(DBG_PTS, " expected: %#B", &hash); + DBG1(DBG_PTS, " received: %#B", &measurement); + status = FAILED; + break; + } + } + e->destroy(e); + + if (status == NOT_FOUND) + { + DBG1(DBG_PTS, "PCR %2d no measurement #%d " + "found in database", pcr, seq_no); + } + + return status; +} + +METHOD(pts_database_t, insert_comp_measurement, status_t, + private_pts_database_t *this, chunk_t measurement, int cid, int kid, + int seq_no, int pcr, pts_meas_algorithms_t algo) +{ + int id; + + if (this->db->execute(this->db, &id, + "INSERT INTO component_hashes " + "(component, key, seq_no, pcr, algo, hash) " + "VALUES (?, ?, ?, ?, ?, ?)", + DB_INT, cid, DB_INT, kid, DB_INT, seq_no, DB_INT, pcr, + DB_INT, algo, DB_BLOB, measurement) == 1) + { + return SUCCESS; + } + + DBG1(DBG_PTS, "could not insert component measurement into database"); + return FAILED; +} + +METHOD(pts_database_t, delete_comp_measurements, int, + private_pts_database_t *this, int cid, int kid) +{ + return this->db->execute(this->db, NULL, + "DELETE FROM component_hashes " + "WHERE component = ? AND key = ?", + DB_INT, cid, DB_INT, kid); +} + +METHOD(pts_database_t, get_comp_measurement_count, status_t, + private_pts_database_t *this, pts_comp_func_name_t *comp_name, + chunk_t keyid, pts_meas_algorithms_t algo, int *cid, int *kid, int *count) +{ + enumerator_t *e; + status_t status = SUCCESS; + + /* Initialize count */ + *count = 0; + + if (_check_aik_keyid(this, keyid, kid) != SUCCESS) + { + return FAILED; + } + + /* Get the primary key of the Component Functional Name */ + e = this->db->query(this->db, + "SELECT id FROM components " + " WHERE vendor_id = ? AND name = ? AND qualifier = ?", + DB_INT, comp_name->get_vendor_id(comp_name), + DB_INT, comp_name->get_name(comp_name), + DB_INT, comp_name->get_qualifier(comp_name), + DB_INT); + if (!e) + { + DBG1(DBG_PTS, "no database query enumerator returned"); + return FAILED; + } + if (!e->enumerate(e, cid)) + { + DBG1(DBG_PTS, "component functional name not found in database"); + e->destroy(e); + return FAILED; + } + e->destroy(e); + + /* Get the number of stored measurements for a given AIK and component */ + e = this->db->query(this->db, + "SELECT COUNT(*) FROM component_hashes AS ch " + "WHERE component = ? AND key = ? AND algo = ?", + DB_INT, *cid, DB_INT, *kid, DB_INT, algo, DB_INT); + if (!e) + { + DBG1(DBG_PTS, "no database query enumerator returned"); + return FAILED; + } + if (!e->enumerate(e, count)) + { + DBG1(DBG_PTS, "no component measurement count returned from database"); + status = FAILED; + } + e->destroy(e); + + return status; +} + METHOD(pts_database_t, destroy, void, private_pts_database_t *this) { @@ -98,8 +285,15 @@ pts_database_t *pts_database_create(char *uri) INIT(this, .public = { - .create_file_enumerator = _create_file_enumerator, - .create_hash_enumerator = _create_hash_enumerator, + .create_file_meas_enumerator = _create_file_meas_enumerator, + .create_file_meta_enumerator = _create_file_meta_enumerator, + .create_comp_evid_enumerator = _create_comp_evid_enumerator, + .create_file_hash_enumerator = _create_file_hash_enumerator, + .check_aik_keyid = _check_aik_keyid, + .check_comp_measurement = _check_comp_measurement, + .insert_comp_measurement = _insert_comp_measurement, + .delete_comp_measurements = _delete_comp_measurements, + .get_comp_measurement_count = _get_comp_measurement_count, .destroy = _destroy, }, .db = lib->db->create(lib->db, uri), @@ -107,8 +301,8 @@ pts_database_t *pts_database_create(char *uri) if (!this->db) { - DBG1(DBG_PTS, "failed to connect to PTS file measurement database '%s'", - uri); + DBG1(DBG_PTS, + "failed to connect to PTS file measurement database '%s'", uri); free(this); return NULL; } diff --git a/src/libpts/pts/pts_database.h b/src/libpts/pts/pts_database.h index f2a6854a5..a9a68ac76 100644 --- a/src/libpts/pts/pts_database.h +++ b/src/libpts/pts/pts_database.h @@ -24,6 +24,7 @@ typedef struct pts_database_t pts_database_t; #include "pts_meas_algo.h" +#include "components/pts_comp_func_name.h" #include <library.h> /** @@ -33,25 +34,107 @@ typedef struct pts_database_t pts_database_t; struct pts_database_t { /** - * Get files to be measured by PTS + * Get files/directories to be measured by PTS * - * @param product software product (os, vpn client, etc.) - * @return enumerator over all matching files + * @param product Software product (os, vpn client, etc.) + * @return Enumerator over all matching files/directories */ - enumerator_t* (*create_file_enumerator)(pts_database_t *this, char *product); + enumerator_t* (*create_file_meas_enumerator)(pts_database_t *this, + char *product); + + /** + * Get files/directories to request metadata of + * + * @param product Software product (os, vpn client, etc.) + * @return Enumerator over all matching files/directories + */ + enumerator_t* (*create_file_meta_enumerator)(pts_database_t *this, + char *product); /** * Get stored measurement hash for single file or directory entries * - * @param product software product (os, vpn client, etc.) - * @param algo hash algorithm used for measurement - * @param id primary key of measured file/directory + * @param product Software product (os, vpn client, etc.) + * @param algo Hash algorithm used for measurement + * @param id Primary key of measured file/directory * @param is_dir TRUE if directory was measured - * @return enumerator over all matching measurement hashes + * @return Enumerator over all matching measurement hashes + */ + enumerator_t* (*create_file_hash_enumerator)(pts_database_t *this, + char *product, pts_meas_algorithms_t algo, + int id, bool is_dir); + + /** + * Check if an AIK given by its keyid is registered in the database + * + * @param keyid AIK keyid (SHA-1 hash of the AIK public key info) + * @param kid Primary key of AIK entry in keys table + * @return SUCCESS if AIK is present, FAILED otherwise + */ + status_t (*check_aik_keyid)(pts_database_t *this, chunk_t keyid, int *kid); + + /** + * Get functional components to request evidence of + * + * @param kid Primary key of AIK entry in keys table + * @return Enumerator over all matching components + */ + enumerator_t* (*create_comp_evid_enumerator)(pts_database_t *this, int kid); + + /** + * Check a functional component measurement against value stored in database + * + * @param measurement measurement hash + * @param cid Primary key of Component Functional Name entry + * @param kid Primary key of AIK entry in keys table + * @param seq_no Measurement sequence number + * @param prc Number of the PCR the measurement was extended into + * @param algo Hash algorithm used for measurement + * @return SUCCESS if check was successful + */ + status_t (*check_comp_measurement)(pts_database_t *this, chunk_t measurement, + int cid, int kid, int seq_no, int pcr, + pts_meas_algorithms_t algo); + + /** + * Insert a functional component measurement into the database + * + * @param measurement Measurement hash + * @param cid Primary key of Component Functional Name entry + * @param kid Primary key of AIK entry in keys table + * @param seq_no Measurement sequence number + * @param prc Number of the PCR the measurement was extended into + * @param algo Hash algorithm used for measurement + * @return SUCCESS if INSERT was successful + */ + status_t (*insert_comp_measurement)(pts_database_t *this, chunk_t measurement, + int cid, int kid, int seq_no, int pcr, + pts_meas_algorithms_t algo); + + /** + * Delete functional component measurements from the database + * + * @param cid Primary key of Component Functional Name entry + * @param kid Primary key of AIK entry in keys table + * @return number of deleted measurement entries + */ + int (*delete_comp_measurements)(pts_database_t *this, int cid, int kid); + + /** + * Get the number of measurements for a functional component and AIK + * + * @param comp_name Component Functional Name + * @param keyid SHA-1 hash of AIK public key info + * @param algo Hash algorithm used for measurement + * @param cid Primary key of Component Functional Name entry + * @param kid Primary key of AIK entry in keys table + * @param count measurement count + * @return SUCCESS if COUNT was successful */ - enumerator_t* (*create_hash_enumerator)(pts_database_t *this, char *product, - pts_meas_algorithms_t algo, - int id, bool is_dir); + status_t (*get_comp_measurement_count)(pts_database_t *this, + pts_comp_func_name_t *comp_name, chunk_t keyid, + pts_meas_algorithms_t algo, int *cid, int *kid, + int *count); /** * Destroys a pts_database_t object. diff --git a/src/libpts/pts/pts_dh_group.c b/src/libpts/pts/pts_dh_group.c new file mode 100644 index 000000000..fb141327f --- /dev/null +++ b/src/libpts/pts/pts_dh_group.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "pts_dh_group.h" + +#include <debug.h> + +/** + * Described in header. + */ +bool pts_dh_group_probe(pts_dh_group_t *dh_groups) +{ + enumerator_t *enumerator; + diffie_hellman_group_t dh_group; + const char *plugin_name; + char format1[] = " %s PTS DH group %N[%s] available"; + char format2[] = " %s PTS DH group %N not available"; + + *dh_groups = PTS_DH_GROUP_NONE; + + enumerator = lib->crypto->create_dh_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &dh_group, &plugin_name)) + { + if (dh_group == MODP_1024_BIT) + { + *dh_groups |= PTS_DH_GROUP_IKE2; + DBG2(DBG_PTS, format1, "optional ", diffie_hellman_group_names, + dh_group, plugin_name); + } + else if (dh_group == MODP_1536_BIT) + { + *dh_groups |= PTS_DH_GROUP_IKE5; + DBG2(DBG_PTS, format1, "optional ", diffie_hellman_group_names, + dh_group, plugin_name); + } + else if (dh_group == MODP_2048_BIT) + { + *dh_groups |= PTS_DH_GROUP_IKE14; + DBG2(DBG_PTS, format1, "optional ", diffie_hellman_group_names, + dh_group, plugin_name); + } + else if (dh_group == ECP_256_BIT) + { + *dh_groups |= PTS_DH_GROUP_IKE19; + DBG2(DBG_PTS, format1, "mandatory", diffie_hellman_group_names, + dh_group, plugin_name); + } + else if (dh_group == ECP_384_BIT) + { + *dh_groups |= PTS_DH_GROUP_IKE20; + DBG2(DBG_PTS, format1, "optional ", diffie_hellman_group_names, + dh_group, plugin_name); + } + } + enumerator->destroy(enumerator); + + if (*dh_groups & PTS_DH_GROUP_IKE19) + { + return TRUE; + } + else + { + DBG1(DBG_PTS, format2, "mandatory", diffie_hellman_group_names, + ECP_256_BIT); + } + return FALSE; +} + +/** + * Described in header. + */ +bool pts_dh_group_update(char *dh_group, pts_dh_group_t *dh_groups) +{ + if (strcaseeq(dh_group, "ecp384")) + { + /* nothing to update, all groups are supported */ + return TRUE; + } + if (strcaseeq(dh_group, "ecp256")) + { + /* remove DH group 20 */ + *dh_groups &= ~PTS_DH_GROUP_IKE20; + return TRUE; + } + if (strcaseeq(dh_group, "modp2048")) + { + /* remove DH groups 19 and 20 */ + *dh_groups &= ~(PTS_DH_GROUP_IKE20 | PTS_DH_GROUP_IKE19); + return TRUE; + } + if (strcaseeq(dh_group, "modp1536")) + { + /* remove DH groups 14, 19 and 20 */ + *dh_groups &= ~(PTS_DH_GROUP_IKE20 | PTS_DH_GROUP_IKE19 | + PTS_DH_GROUP_IKE14); + return TRUE; + } + if (strcaseeq(dh_group, "modp1024")) + { + /* remove DH groups 5, 14, 19 and 20 */ + *dh_groups &= ~(PTS_DH_GROUP_IKE20 | PTS_DH_GROUP_IKE19 | + PTS_DH_GROUP_IKE14 | PTS_DH_GROUP_IKE5); + return TRUE; + } + DBG1(DBG_PTS, "unknown DH group '%s' configured", dh_group); + return FALSE; +} + +/** + * Described in header. + */ +pts_dh_group_t pts_dh_group_select(pts_dh_group_t supported_dh_groups, + pts_dh_group_t offered_dh_groups) +{ + if ((supported_dh_groups & PTS_DH_GROUP_IKE20) && + (offered_dh_groups & PTS_DH_GROUP_IKE20)) + { + return PTS_DH_GROUP_IKE20; + } + if ((supported_dh_groups & PTS_DH_GROUP_IKE19) && + (offered_dh_groups & PTS_DH_GROUP_IKE19)) + { + return PTS_DH_GROUP_IKE19; + } + if ((supported_dh_groups & PTS_DH_GROUP_IKE14) && + (offered_dh_groups & PTS_DH_GROUP_IKE14)) + { + return PTS_DH_GROUP_IKE14; + } + if ((supported_dh_groups & PTS_DH_GROUP_IKE5) && + (offered_dh_groups & PTS_DH_GROUP_IKE5)) + { + return PTS_DH_GROUP_IKE5; + } + if ((supported_dh_groups & PTS_DH_GROUP_IKE2) && + (offered_dh_groups & PTS_DH_GROUP_IKE2)) + { + return PTS_DH_GROUP_IKE2; + } + return PTS_DH_GROUP_NONE; +} + +/** + * Described in header. + */ +diffie_hellman_group_t pts_dh_group_to_ike(pts_dh_group_t dh_group) +{ + switch (dh_group) + { + case PTS_DH_GROUP_IKE2: + return MODP_1024_BIT; + case PTS_DH_GROUP_IKE5: + return MODP_1536_BIT; + case PTS_DH_GROUP_IKE14: + return MODP_2048_BIT; + case PTS_DH_GROUP_IKE19: + return ECP_256_BIT; + case PTS_DH_GROUP_IKE20: + return ECP_384_BIT; + default: + return MODP_NONE; + } +} diff --git a/src/libpts/pts/pts_dh_group.h b/src/libpts/pts/pts_dh_group.h new file mode 100644 index 000000000..8664a4b84 --- /dev/null +++ b/src/libpts/pts/pts_dh_group.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_dh_group pts_dh_group + * @{ @ingroup pts + */ + +#ifndef PTS_DH_GROUP_H_ +#define PTS_DH_GROUP_H_ + +#include <library.h> +#include <crypto/diffie_hellman.h> + +typedef enum pts_dh_group_t pts_dh_group_t; + +/** + * PTS Diffie Hellman Group Values + */ +enum pts_dh_group_t { + /** No DH Group */ + PTS_DH_GROUP_NONE = 0, + /** IKE Group 2 */ + PTS_DH_GROUP_IKE2 = (1<<15), + /** IKE Group 5 */ + PTS_DH_GROUP_IKE5 = (1<<14), + /** IKE Group 14 */ + PTS_DH_GROUP_IKE14 = (1<<13), + /** IKE Group 19 */ + PTS_DH_GROUP_IKE19 = (1<<12), + /** IKE Group 20 */ + PTS_DH_GROUP_IKE20 = (1<<11), +}; + +/** + * Diffie-Hellman Group Values + * see section 3.8.6 of PTS Protocol: Binding to TNC IF-M Specification + * + * 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |1|2|3|4|5|R|R|R|R|R|R|R|R|R|R|R| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +/** + * Probe available PTS Diffie-Hellman groups + * + * @param dh_groups returns set of available DH groups + * @return TRUE if mandatory DH groups are available + */ +bool pts_dh_group_probe(pts_dh_group_t *dh_groups); + +/** + * Update supported Diffie-Hellman groups according to configuration + * + * modp1024: PTS_DH_GROUP_IKE2 + * modp1536: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 + * modp2048: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 + * ecp256: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 | + * PTS_DH_GROUP_IKE19 + * ecp384: PTS_DH_GROUP_IKE2 | PTS_DH_GROUP_IKE5 | PTS_DH_GROUP_IKE14 | + * PTS_DH_GROUP_IKE19 | PTS_DH_GROUP_IKE20 + * + * The PTS-IMC is expected to select the strongest supported group + * + * @param dh_group configured DH group + * @param dh_groups returns set of available DH groups + */ +bool pts_dh_group_update(char *dh_group, pts_dh_group_t *dh_groups); + +/** + * Select the strongest supported Diffie-Hellman group + * among a set of offered DH groups + * + * @param supported_groups set of supported DH groups + * @param offered_groups set of offered DH groups + * @return selected DH group + */ +pts_dh_group_t pts_dh_group_select(pts_dh_group_t supported_dh_groups, + pts_dh_group_t offered_dh_groups); + +/** + * Convert pts_dh_group_t to diffie_hellman_group_t + * + * @param dh_group PTS DH group type + * @return IKE DH group type + */ +diffie_hellman_group_t pts_dh_group_to_ike(pts_dh_group_t dh_group); + +#endif /** PTS_DH_GROUP_H_ @}*/ diff --git a/src/libpts/pts/pts_error.c b/src/libpts/pts/pts_error.c index ec1e6c014..6e914b2a9 100644 --- a/src/libpts/pts/pts_error.c +++ b/src/libpts/pts/pts_error.c @@ -56,4 +56,44 @@ pa_tnc_attr_t* pts_hash_alg_error_create(pts_meas_algorithms_t algorithms) writer->destroy(writer); return attr; -}
\ No newline at end of file +} + +/** + * Described in header. + */ +pa_tnc_attr_t* pts_dh_group_error_create(pts_dh_group_t dh_groups) +{ + bio_writer_t *writer; + chunk_t msg_info; + pa_tnc_attr_t *attr; + + writer = bio_writer_create(4); + writer->write_uint16(writer, 0x0000); + writer->write_uint16(writer, dh_groups); + msg_info = writer->get_buf(writer); + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, TCG_PTS_DH_GRPS_NOT_SUPPORTED, + msg_info); + writer->destroy(writer); + + return attr; +} + +/** + * Described in header. + */ +pa_tnc_attr_t* pts_dh_nonce_error_create(int min_nonce_len, int max_nonce_len) +{ + bio_writer_t *writer; + chunk_t msg_info; + pa_tnc_attr_t *attr; + + writer = bio_writer_create(4); + writer->write_uint16(writer, min_nonce_len); + writer->write_uint16(writer, max_nonce_len); + msg_info = writer->get_buf(writer); + attr = ietf_attr_pa_tnc_error_create(PEN_TCG, TCG_PTS_BAD_NONCE_LENGTH, + msg_info); + writer->destroy(writer); + + return attr; +} diff --git a/src/libpts/pts/pts_error.h b/src/libpts/pts/pts_error.h index 6eeab0792..9a53abd98 100644 --- a/src/libpts/pts/pts_error.h +++ b/src/libpts/pts/pts_error.h @@ -24,32 +24,36 @@ typedef enum pts_error_code_t pts_error_code_t; #include "pts_meas_algo.h" +#include "pts_dh_group.h" #include "pa_tnc/pa_tnc_attr.h" #include <library.h> +#define PTS_MIN_NONCE_LEN 17 +#define PTS_MAX_NONCE_LEN 0xffff + /** * PTS Attestation Error Codes * see section 3.14.2 of PTS Protocol: Binding to TNC IF-M Specification */ enum pts_error_code_t { - TCG_PTS_RESERVED_ERROR = 0, + TCG_PTS_RESERVED_ERROR = 0, TCG_PTS_HASH_ALG_NOT_SUPPORTED = 1, - TCG_PTS_INVALID_PATH = 2, - TCG_PTS_FILE_NOT_FOUND = 3, - TCG_PTS_REG_NOT_SUPPORTED = 4, - TCG_PTS_REG_KEY_NOT_FOUND = 5, - TCG_PTS_DH_GRPS_NOT_SUPPORTED = 6, - TCG_PTS_BAD_NONCE_LENGTH = 7, - TCG_PTS_INVALID_NAME_FAM = 8, + TCG_PTS_INVALID_PATH = 2, + TCG_PTS_FILE_NOT_FOUND = 3, + TCG_PTS_REG_NOT_SUPPORTED = 4, + TCG_PTS_REG_KEY_NOT_FOUND = 5, + TCG_PTS_DH_GRPS_NOT_SUPPORTED = 6, + TCG_PTS_BAD_NONCE_LENGTH = 7, + TCG_PTS_INVALID_NAME_FAM = 8, TCG_PTS_TPM_VERS_NOT_SUPPORTED = 9, - TCG_PTS_INVALID_DELIMITER = 10, + TCG_PTS_INVALID_DELIMITER = 10, TCG_PTS_OPERATION_NOT_SUPPORTED = 11, - TCG_PTS_RM_ERROR = 12, - TCG_PTS_UNABLE_LOCAL_VAL = 13, - TCG_PTS_UNABLE_CUR_EVID = 14, - TCG_PTS_UNABLE_DET_TTC = 15, - TCG_PTS_UNABLE_DET_PCR = 16, + TCG_PTS_RM_ERROR = 12, + TCG_PTS_UNABLE_LOCAL_VAL = 13, + TCG_PTS_UNABLE_CUR_EVID = 14, + TCG_PTS_UNABLE_DET_TTC = 15, + TCG_PTS_UNABLE_DET_PCR = 16, }; /** @@ -61,8 +65,25 @@ extern enum_name_t *pts_error_code_names; * Creates a PTS Hash Algorithm Not Supported Error Attribute * see section 4.2.2 of PTS Protocol: Binding to TNC IF-M Specification * - * @param algorithms supported measurement hash algorithms + * @param algorithms supported measurement hash algorithms */ pa_tnc_attr_t* pts_hash_alg_error_create(pts_meas_algorithms_t algorithms); +/** + * Creates a PTS DH Group Not Supported Error Attribute + * see section 4.2.4 of PTS Protocol: Binding to TNC IF-M Specification + * + * @param dh_groups supported DH groups + */ +pa_tnc_attr_t* pts_dh_group_error_create(pts_dh_group_t dh_groups); + +/** + * Creates a PTS DH PN Nonce Not Supported Error Attribute + * see section 4.2.5 of PTS Protocol: Binding to TNC IF-M Specification + * + * @param min_nonce_len minimum nonce length + * @param max_nonce_len maximum nonce length + */ +pa_tnc_attr_t* pts_dh_nonce_error_create(int min_nonce_len, int max_nonce_len); + #endif /** PTS_ERROR_H_ @}*/ diff --git a/src/libpts/pts/pts_file_meta.c b/src/libpts/pts/pts_file_meta.c index 0924b3b25..6ed1c01b4 100644 --- a/src/libpts/pts/pts_file_meta.c +++ b/src/libpts/pts/pts_file_meta.c @@ -56,54 +56,15 @@ METHOD(pts_file_meta_t, get_file_count, int, } METHOD(pts_file_meta_t, add, void, - private_pts_file_meta_t *this, char *filename, pts_file_type_t type, - u_int64_t filesize, time_t create_time, time_t last_modify_time, time_t last_access_time, - u_int64_t owner_id, u_int64_t group_id) + private_pts_file_meta_t *this, pts_file_metadata_t *metadata) { - pts_file_metadata_t *entry; - - entry = malloc_thing(pts_file_metadata_t); - - entry->filename = strdup(filename); - entry->meta_length = PTS_FILE_METADATA_SIZE + strlen(entry->filename); - entry->type = type; - entry->filesize = filesize; - entry->create_time = create_time; - entry->last_modify_time = last_modify_time; - entry->last_access_time = last_access_time; - entry->owner_id = owner_id; - entry->group_id = group_id; - - this->list->insert_last(this->list, entry); -} - -/** - * Enumerate file metadata entries - */ -static bool entry_filter(void *null, pts_file_metadata_t **entry, - char **filename, void *i2, u_int16_t *meta_length, void *i3, - pts_file_type_t *type, void *i4, u_int64_t *filesize, void *i5, - time_t *create_time, void *i6, time_t *last_modify_time, void *i7, - time_t *last_access_time, void *i8, u_int64_t *owner_id, void *i9, - u_int64_t *group_id) -{ - *filename = (*entry)->filename; - *meta_length = (*entry)->meta_length; - *type = (*entry)->type; - *filesize = (*entry)->filesize; - *create_time = (*entry)->create_time; - *last_modify_time = (*entry)->last_modify_time; - *last_access_time = (*entry)->last_access_time; - *owner_id = (*entry)->owner_id; - *group_id = (*entry)->group_id; - return TRUE; + this->list->insert_last(this->list, metadata); } METHOD(pts_file_meta_t, create_enumerator, enumerator_t*, private_pts_file_meta_t *this) { - return enumerator_create_filter(this->list->create_enumerator(this->list), - (void*)entry_filter, NULL, NULL); + return this->list->create_enumerator(this->list); } METHOD(pts_file_meta_t, destroy, void, diff --git a/src/libpts/pts/pts_file_meta.h b/src/libpts/pts/pts_file_meta.h index 36a4b6294..3f1813306 100644 --- a/src/libpts/pts/pts_file_meta.h +++ b/src/libpts/pts/pts_file_meta.h @@ -29,22 +29,18 @@ typedef struct pts_file_meta_t pts_file_meta_t; typedef struct pts_file_metadata_t pts_file_metadata_t; -/* Without filename field included */ -#define PTS_FILE_METADATA_SIZE 52 - /** * Structure holding file metadata */ struct pts_file_metadata_t { - u_int16_t meta_length; - pts_file_type_t type; - u_int64_t filesize; - time_t create_time; - time_t last_modify_time; - time_t last_access_time; - u_int64_t owner_id; - u_int64_t group_id; - char *filename; + pts_file_type_t type; + u_int64_t filesize; + u_int64_t created; + u_int64_t modified; + u_int64_t accessed; + u_int64_t owner; + u_int64_t group; + char *filename; }; /** @@ -60,14 +56,12 @@ struct pts_file_meta_t { int (*get_file_count)(pts_file_meta_t *this); /** - * Add a PTS File Metadata + * Add PTS File Metadata * * @param filename Name of measured file or directory * @param metadata File metadata */ - void (*add)(pts_file_meta_t *this, char *filename, pts_file_type_t type, - u_int64_t filesize, time_t create_time, time_t last_modfy_time, time_t last_access_time, - u_int64_t owner_id, u_int64_t group_id); + void (*add)(pts_file_meta_t *this, pts_file_metadata_t *metadata); /** * Create a PTS File Metadata enumerator diff --git a/src/libpts/pts/pts_file_type.c b/src/libpts/pts/pts_file_type.c new file mode 100644 index 000000000..fe849dea4 --- /dev/null +++ b/src/libpts/pts/pts_file_type.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 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 "pts_file_type.h" + +ENUM(pts_file_type_names, PTS_FILE_OTHER, PTS_FILE_SOCKET, + "Other", + "FIFO", + "Character-Special", + "Reserved-3", + "Directory", + "Reserved-5", + "Block-Special", + "Reserved-7", + "Regular", + "Reserved-9", + "Symbolic-Link", + "Reserved-11", + "Socket" +); + diff --git a/src/libpts/pts/pts_file_type.h b/src/libpts/pts/pts_file_type.h index f3c5b94dd..c1d236888 100644 --- a/src/libpts/pts/pts_file_type.h +++ b/src/libpts/pts/pts_file_type.h @@ -21,6 +21,8 @@ #ifndef PTS_FILE_TYPE_H_ #define PTS_FILE_TYPE_H_ +#include <library.h> + typedef enum pts_file_type_t pts_file_type_t; /** @@ -28,22 +30,34 @@ typedef enum pts_file_type_t pts_file_type_t; * see section 3.17.3 of PTS Protocol: Binding to TNC IF-M Specification */ enum pts_file_type_t { - /** Ignore */ + /** Either unknown or different from standardized types */ PTS_FILE_OTHER = 0x0000, - /** CRTM */ + /** Pipe communication file */ PTS_FILE_FIFO = 0x0001, - /** BIOS */ + /** Character special file */ PTS_FILE_CHAR_SPEC = 0x0002, - /** Platform Extensions */ + /** Reserved */ + PTS_FILE_RESERVED_3 = 0x0003, + /** Directory */ PTS_FILE_DIRECTORY = 0x0004, - /** Motherboard firmware */ + /** Reserved */ + PTS_FILE_RESERVED_5 = 0x0005, + /** Block special file */ PTS_FILE_BLOCK_SPEC = 0x0006, - /** Initial Program Loader */ + /** Reserved */ + PTS_FILE_RESERVED_7 = 0x0007, + /** Regular file */ PTS_FILE_REGULAR = 0x0008, - /** Option ROMs */ + /** Reserved */ + PTS_FILE_RESERVED_9 = 0x0009, + /** Symbolic link */ PTS_FILE_SYM_LINK = 0x000A, - /** Option ROMs */ + /** Reserved */ + PTS_FILE_RESERVED_11 = 0x000B, + /** Socket communication special file */ PTS_FILE_SOCKET = 0x000C, }; +extern enum_name_t *pts_file_type_names; + #endif /** PTS_FILE_TYPE_H_ @}*/ diff --git a/src/libpts/pts/pts_funct_comp_name.h b/src/libpts/pts/pts_funct_comp_name.h deleted file mode 100644 index 0926a2bc7..000000000 --- a/src/libpts/pts/pts_funct_comp_name.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2011 Sansar Choinyambuu - * 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 pts_funct_comp_name pts_funct_comp_name - * @{ @ingroup pts - */ - -#ifndef PTS_FUNCT_COMP_NAME_H_ -#define PTS_FUNCT_COMP_NAME_H_ - -typedef enum pts_funct_comp_type_t pts_funct_comp_type_t; -typedef enum pts_funct_comp_name_t pts_funct_comp_name_t; -typedef struct pts_qualifier_t pts_qualifier_t; - -/** - * PTS Component Functional Type for Qualifier field - */ -enum pts_funct_comp_type_t { - /** Unknown */ - PTS_FUNC_COMP_TYPE_UNKNOWN = 0x0, - /** Trusted Platform */ - PTS_FUNC_COMP_TYPE_TRUSTED = 0x1, - /** Operating System */ - PTS_FUNC_COMP_TYPE_OS = 0x2, - /** Graphical User Interface */ - PTS_FUNC_COMP_TYPE_GUI = 0x3, - /** Application */ - PTS_FUNC_COMP_TYPE_APP = 0x4, - /** Networking */ - PTS_FUNC_COMP_TYPE_NET = 0x5, - /** Library */ - PTS_FUNC_COMP_TYPE_LIB = 0x6, - /** TNC Defined Component */ - PTS_FUNC_COMP_TYPE_TNC = 0x7, - /** All matching Components */ - PTS_FUNC_COMP_TYPE_ALL = 0xF, -}; - -/** - * PTS Component Functional Name Binary Enumeration - */ -enum pts_funct_comp_name_t { - /** Ignore */ - PTS_FUNC_COMP_NAME_IGNORE = 0x0000, - /** CRTM */ - PTS_FUNC_COMP_NAME_CRTM = 0x0001, - /** BIOS */ - PTS_FUNC_COMP_NAME_BIOS = 0x0002, - /** Platform Extensions */ - PTS_FUNC_COMP_NAME_PLATFORM_EXT = 0x0003, - /** Motherboard firmware */ - PTS_FUNC_COMP_NAME_BOARD = 0x0004, - /** Initial Program Loader */ - PTS_FUNC_COMP_NAME_INIT_LOADER = 0x0005, - /** Option ROMs */ - PTS_FUNC_COMP_NAME_OPT_ROMS = 0x0006, -}; - -/** - * Qualifier for Functional Component - */ -struct pts_qualifier_t { - bool kernel; - bool sub_component; - pts_funct_comp_type_t type; -}; - -#endif /** PTS_FUNCT_COMP_NAME_H_ @}*/ diff --git a/src/libpts/pts/pts_meas_algo.c b/src/libpts/pts/pts_meas_algo.c index 260c844d8..865857d3c 100644 --- a/src/libpts/pts/pts_meas_algo.c +++ b/src/libpts/pts/pts_meas_algo.c @@ -17,10 +17,17 @@ #include <debug.h> +ENUM(pts_meas_algorithm_names, PTS_MEAS_ALGO_NONE, PTS_MEAS_ALGO_SHA384, + "None", + "SHA1", + "SHA256", + "SHA384" +); + /** * Described in header. */ -bool pts_meas_probe_algorithms(pts_meas_algorithms_t *algorithms) +bool pts_meas_algo_probe(pts_meas_algorithms_t *algorithms) { enumerator_t *enumerator; hash_algorithm_t hash_alg; @@ -77,7 +84,57 @@ bool pts_meas_probe_algorithms(pts_meas_algorithms_t *algorithms) /** * Described in header. */ -hash_algorithm_t pts_meas_to_hash_algorithm(pts_meas_algorithms_t algorithm) +bool pts_meas_algo_update(char *hash_alg, pts_meas_algorithms_t *algorithms) +{ + if (strcaseeq(hash_alg, "sha384") || strcaseeq(hash_alg, "sha2_384")) + { + /* nothing to update, all algorithms are supported */ + return TRUE; + } + if (strcaseeq(hash_alg, "sha256") || strcaseeq(hash_alg, "sha2_256")) + { + /* remove SHA384algorithm */ + *algorithms &= ~PTS_MEAS_ALGO_SHA384; + return TRUE; + } + if (strcaseeq(hash_alg, "sha1")) + { + /* remove SHA384 and SHA256 algorithms */ + *algorithms &= ~(PTS_MEAS_ALGO_SHA384 | PTS_MEAS_ALGO_SHA256); + return TRUE; + } + DBG1(DBG_PTS, "unknown hash algorithm '%s' configured", hash_alg); + return FALSE; +} + +/** + * Described in header. + */ +pts_meas_algorithms_t pts_meas_algo_select(pts_meas_algorithms_t supported_algos, + pts_meas_algorithms_t offered_algos) +{ + if ((supported_algos & PTS_MEAS_ALGO_SHA384) && + (offered_algos & PTS_MEAS_ALGO_SHA384)) + { + return PTS_MEAS_ALGO_SHA384; + } + if ((supported_algos & PTS_MEAS_ALGO_SHA256) && + (offered_algos & PTS_MEAS_ALGO_SHA256)) + { + return PTS_MEAS_ALGO_SHA256; + } + if ((supported_algos & PTS_MEAS_ALGO_SHA1) && + (offered_algos & PTS_MEAS_ALGO_SHA1)) + { + return PTS_MEAS_ALGO_SHA1; + } + return PTS_MEAS_ALGO_NONE; +} + +/** + * Described in header. + */ +hash_algorithm_t pts_meas_algo_to_hash(pts_meas_algorithms_t algorithm) { switch (algorithm) { @@ -91,3 +148,23 @@ hash_algorithm_t pts_meas_to_hash_algorithm(pts_meas_algorithms_t algorithm) return HASH_UNKNOWN; } } + +/** + * Described in header. + */ +size_t pts_meas_algo_hash_size(pts_meas_algorithms_t algorithm) +{ + switch (algorithm) + { + case PTS_MEAS_ALGO_SHA1: + return HASH_SIZE_SHA1; + case PTS_MEAS_ALGO_SHA256: + return HASH_SIZE_SHA256; + case PTS_MEAS_ALGO_SHA384: + return HASH_SIZE_SHA384; + case PTS_MEAS_ALGO_NONE: + default: + return 0; + } +} + diff --git a/src/libpts/pts/pts_meas_algo.h b/src/libpts/pts/pts_meas_algo.h index 6aa0ce695..1d96a4946 100644 --- a/src/libpts/pts/pts_meas_algo.h +++ b/src/libpts/pts/pts_meas_algo.h @@ -30,12 +30,18 @@ typedef enum pts_meas_algorithms_t pts_meas_algorithms_t; * PTS Measurement Algorithms */ enum pts_meas_algorithms_t { - PTS_MEAS_ALGO_SHA1 = (1<<15), - PTS_MEAS_ALGO_SHA256 = (1<<14), - PTS_MEAS_ALGO_SHA384 = (1<<13), + PTS_MEAS_ALGO_NONE = 0, + PTS_MEAS_ALGO_SHA1 = (1<<15), + PTS_MEAS_ALGO_SHA256 = (1<<14), + PTS_MEAS_ALGO_SHA384 = (1<<13), }; /** + * enum name for pts_meas_algorithms_t. + */ +extern enum_name_t *pts_meas_algorithm_names; + +/** * Diffie-Hellman Hash Algorithm Values * see section 3.8.5 of PTS Protocol: Binding to TNC IF-M Specification * @@ -53,7 +59,32 @@ enum pts_meas_algorithms_t { * @param algorithms set of available algorithms * @return TRUE if mandatory algorithms are available */ -bool pts_meas_probe_algorithms(pts_meas_algorithms_t *algorithms); +bool pts_meas_algo_probe(pts_meas_algorithms_t *algorithms); + +/** + * Update supported PTS measurement algorithms according to configuration + * + * sha1 : PTS_MEAS_ALGO_SHA1 + * sha256: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 + * sha384: PTS_MEAS_ALGO_SHA1 | PTS_MEAS_ALGO_SHA256 | PTS_MEAS_ALGO_SHA384 + * + * The PTS-IMC is expected to select the strongest supported algorithm + * + * @param hash_alg configured hash algorithm + * @param algorithms returns set of available PTS measurement algorithms + */ +bool pts_meas_algo_update(char *hash_alg, pts_meas_algorithms_t *algorithms); + +/** + * Select the strongest PTS measurement algorithm + * among a set of offered PTS measurement algorithms + * + * @param supported_algos set of supported PTS measurement algorithms + * @param offered_algos set of offered PTS measurements algorithms + * @return selected algorithm + */ +pts_meas_algorithms_t pts_meas_algo_select(pts_meas_algorithms_t supported_algos, + pts_meas_algorithms_t offered_algos); /** * Convert pts_meas_algorithms_t to hash_algorithm_t @@ -61,6 +92,14 @@ bool pts_meas_probe_algorithms(pts_meas_algorithms_t *algorithms); * @param algorithm PTS measurement algorithm type * @return libstrongswan hash algorithm type */ -hash_algorithm_t pts_meas_to_hash_algorithm(pts_meas_algorithms_t algorithm); +hash_algorithm_t pts_meas_algo_to_hash(pts_meas_algorithms_t algorithm); + +/** + * Return the hash size of a pts_meas_algorithm + * + * @param algorithm PTS measurement algorithm type + * @return hash size in bytes + */ +size_t pts_meas_algo_hash_size(pts_meas_algorithms_t algorithm); #endif /** PTS_MEAS_ALGO_H_ @}*/ diff --git a/src/libpts/pts/pts_req_func_comp_evid.h b/src/libpts/pts/pts_req_func_comp_evid.h new file mode 100644 index 000000000..bbf5bbf5b --- /dev/null +++ b/src/libpts/pts/pts_req_func_comp_evid.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_req_func_comp_evid pts_req_func_comp_evid + * @{ @ingroup pts + */ + +#ifndef PTS_REQ_FUNC_COMP_EVID_H_ +#define PTS_REQ_FUNC_COMP_EVID_H_ + +typedef enum pts_req_func_comp_evid_t pts_req_func_comp_evid_t; + +#include <library.h> + +/** + * PTS Request Functional Component Evidence Flags + */ +enum pts_req_func_comp_evid_t { + /** Transitive Trust Chain flag */ + PTS_REQ_FUNC_COMP_EVID_TTC = (1<<7), + /** Verify Component flag */ + PTS_REQ_FUNC_COMP_EVID_VER = (1<<6), + /** Current Evidence flag */ + PTS_REQ_FUNC_COMP_EVID_CURR = (1<<5), + /** PCR Information flag */ + PTS_REQ_FUNC_COMP_EVID_PCR = (1<<4), +}; + +#endif /** PTS_FUNCT_COMP_EVID_REQ_H_ @}*/ diff --git a/src/libpts/pts/pts_simple_evid_final.h b/src/libpts/pts/pts_simple_evid_final.h new file mode 100644 index 000000000..0c8dea0cc --- /dev/null +++ b/src/libpts/pts/pts_simple_evid_final.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 pts_simple_evid_final pts_rsimple_evid_final + * @{ @ingroup pts + */ + +#ifndef PTS_SIMPLE_EVID_FINAL_H_ +#define PTS_SIMPLE_EVID_FINAL_H_ + +typedef enum pts_simple_evid_final_flag_t pts_simple_evid_final_flag_t; + +#include <library.h> + +/** + * PTS Simple Evidence Final Flags + */ +enum pts_simple_evid_final_flag_t { + /** TPM PCR Composite and TPM Quote Signature not included */ + PTS_SIMPLE_EVID_FINAL_NO = 0x00, + /** TPM PCR Composite and TPM Quote Signature included + * using TPM_QUOTE_INFO */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO = 0x40, + /** TPM PCR Composite and TPM Quote Signature included + * using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO not appended */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2 = 0x80, + /** TPM PCR Composite and TPM Quote Signature included + * using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO appended */ + PTS_SIMPLE_EVID_FINAL_QUOTE_INFO2_CAP_VER = 0xC0, + /** Evidence Signature included */ + PTS_SIMPLE_EVID_FINAL_EVID_SIG = 0x20, +}; + +#endif /** PTS_SIMPLE_EVID_FINAL_H_ @}*/ diff --git a/src/libpts/tcg/tcg_attr.c b/src/libpts/tcg/tcg_attr.c index 51acb6792..656791a8f 100644 --- a/src/libpts/tcg/tcg_attr.c +++ b/src/libpts/tcg/tcg_attr.c @@ -14,24 +14,29 @@ #include "tcg_attr.h" #include "tcg/tcg_pts_attr_proto_caps.h" +#include "tcg/tcg_pts_attr_dh_nonce_params_req.h" +#include "tcg/tcg_pts_attr_dh_nonce_params_resp.h" +#include "tcg/tcg_pts_attr_dh_nonce_finish.h" #include "tcg/tcg_pts_attr_meas_algo.h" #include "tcg/tcg_pts_attr_get_tpm_version_info.h" #include "tcg/tcg_pts_attr_tpm_version_info.h" #include "tcg/tcg_pts_attr_get_aik.h" #include "tcg/tcg_pts_attr_aik.h" -#include "tcg/tcg_pts_attr_req_funct_comp_evid.h" +#include "tcg/tcg_pts_attr_req_func_comp_evid.h" #include "tcg/tcg_pts_attr_gen_attest_evid.h" #include "tcg/tcg_pts_attr_simple_comp_evid.h" #include "tcg/tcg_pts_attr_simple_evid_final.h" #include "tcg/tcg_pts_attr_req_file_meas.h" #include "tcg/tcg_pts_attr_file_meas.h" +#include "tcg/tcg_pts_attr_req_file_meta.h" +#include "tcg/tcg_pts_attr_unix_file_meta.h" -ENUM_BEGIN(tcg_attr_names, TCG_PTS_REQ_FUNCT_COMP_EVID, - TCG_PTS_REQ_FUNCT_COMP_EVID, +ENUM_BEGIN(tcg_attr_names, TCG_PTS_REQ_FUNC_COMP_EVID, + TCG_PTS_REQ_FUNC_COMP_EVID, "Request Functional Component Evidence"); ENUM_NEXT(tcg_attr_names, TCG_PTS_GEN_ATTEST_EVID, TCG_PTS_GEN_ATTEST_EVID, - TCG_PTS_REQ_FUNCT_COMP_EVID, + TCG_PTS_REQ_FUNC_COMP_EVID, "Generate Attestation Evidence"); ENUM_NEXT(tcg_attr_names, TCG_PTS_SIMPLE_COMP_EVID, TCG_PTS_SIMPLE_COMP_EVID, @@ -154,6 +159,12 @@ pa_tnc_attr_t* tcg_attr_create_from_data(u_int32_t type, chunk_t value) return tcg_pts_attr_proto_caps_create_from_data(value, TRUE); case TCG_PTS_PROTO_CAPS: return tcg_pts_attr_proto_caps_create_from_data(value, FALSE); + case TCG_PTS_DH_NONCE_PARAMS_REQ: + return tcg_pts_attr_dh_nonce_params_req_create_from_data(value); + case TCG_PTS_DH_NONCE_PARAMS_RESP: + return tcg_pts_attr_dh_nonce_params_resp_create_from_data(value); + case TCG_PTS_DH_NONCE_FINISH: + return tcg_pts_attr_dh_nonce_finish_create_from_data(value); case TCG_PTS_MEAS_ALGO: return tcg_pts_attr_meas_algo_create_from_data(value, FALSE); case TCG_PTS_MEAS_ALGO_SELECTION: @@ -166,8 +177,8 @@ pa_tnc_attr_t* tcg_attr_create_from_data(u_int32_t type, chunk_t value) return tcg_pts_attr_get_aik_create_from_data(value); case TCG_PTS_AIK: return tcg_pts_attr_aik_create_from_data(value); - case TCG_PTS_REQ_FUNCT_COMP_EVID: - return tcg_pts_attr_req_funct_comp_evid_create_from_data(value); + case TCG_PTS_REQ_FUNC_COMP_EVID: + return tcg_pts_attr_req_func_comp_evid_create_from_data(value); case TCG_PTS_GEN_ATTEST_EVID: return tcg_pts_attr_gen_attest_evid_create_from_data(value); case TCG_PTS_SIMPLE_COMP_EVID: @@ -178,17 +189,16 @@ pa_tnc_attr_t* tcg_attr_create_from_data(u_int32_t type, chunk_t value) return tcg_pts_attr_req_file_meas_create_from_data(value); case TCG_PTS_FILE_MEAS: return tcg_pts_attr_file_meas_create_from_data(value); - case TCG_PTS_DH_NONCE_PARAMS_REQ: - case TCG_PTS_DH_NONCE_PARAMS_RESP: - case TCG_PTS_DH_NONCE_FINISH: + case TCG_PTS_REQ_FILE_META: + return tcg_pts_attr_req_file_meta_create_from_data(value); + case TCG_PTS_UNIX_FILE_META: + return tcg_pts_attr_unix_file_meta_create_from_data(value); case TCG_PTS_REQ_TEMPL_REF_MANI_SET_META: case TCG_PTS_TEMPL_REF_MANI_SET_META: case TCG_PTS_UPDATE_TEMPL_REF_MANI: case TCG_PTS_VERIFICATION_RESULT: case TCG_PTS_INTEG_REPORT: - case TCG_PTS_REQ_FILE_META: case TCG_PTS_WIN_FILE_META: - case TCG_PTS_UNIX_FILE_META: case TCG_PTS_REQ_REGISTRY_VALUE: case TCG_PTS_REGISTRY_VALUE: case TCG_PTS_REQ_INTEG_MEAS_LOG: diff --git a/src/libpts/tcg/tcg_attr.h b/src/libpts/tcg/tcg_attr.h index 71ecc1e0a..b45e1488f 100644 --- a/src/libpts/tcg/tcg_attr.h +++ b/src/libpts/tcg/tcg_attr.h @@ -48,7 +48,7 @@ enum tcg_attr_t { TCG_PTS_AIK = 0x0E000000, /* PTS-based Attestation Evidence */ - TCG_PTS_REQ_FUNCT_COMP_EVID = 0x00100000, + TCG_PTS_REQ_FUNC_COMP_EVID = 0x00100000, TCG_PTS_GEN_ATTEST_EVID = 0x00200000, TCG_PTS_SIMPLE_COMP_EVID = 0x00300000, TCG_PTS_SIMPLE_EVID_FINAL = 0x00400000, diff --git a/src/libpts/tcg/tcg_pts_attr_aik.c b/src/libpts/tcg/tcg_pts_attr_aik.c index ffef15f29..9be3794b6 100644 --- a/src/libpts/tcg/tcg_pts_attr_aik.c +++ b/src/libpts/tcg/tcg_pts_attr_aik.c @@ -72,6 +72,11 @@ struct private_tcg_pts_attr_aik_t { * AIK Certificate or Public Key */ certificate_t *aik; + + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -126,6 +131,7 @@ METHOD(pa_tnc_attr_t, build, void, writer->write_uint8(writer, flags); writer->write_data (writer, aik_blob); this->value = chunk_clone(writer->get_buf(writer)); + free(aik_blob.ptr); writer->destroy(writer); } @@ -162,12 +168,22 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_aik_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_aik_t *this) { - DESTROY_IF(this->aik); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + DESTROY_IF(this->aik); + free(this->value.ptr); + free(this); + } } METHOD(tcg_pts_attr_aik_t, get_aik, certificate_t*, @@ -193,6 +209,7 @@ pa_tnc_attr_t *tcg_pts_attr_aik_create(certificate_t *aik) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_aik = _get_aik, @@ -200,6 +217,7 @@ pa_tnc_attr_t *tcg_pts_attr_aik_create(certificate_t *aik) .vendor_id = PEN_TCG, .type = TCG_PTS_AIK, .aik = aik->get_ref(aik), + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -223,6 +241,7 @@ pa_tnc_attr_t *tcg_pts_attr_aik_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_aik = _get_aik, @@ -230,6 +249,7 @@ pa_tnc_attr_t *tcg_pts_attr_aik_create_from_data(chunk_t data) .vendor_id = PEN_TCG, .type = TCG_PTS_AIK, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_dh_nonce_finish.c b/src/libpts/tcg/tcg_pts_attr_dh_nonce_finish.c new file mode 100644 index 000000000..dce98e87d --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_dh_nonce_finish.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "tcg_pts_attr_dh_nonce_finish.h" + +#include <pa_tnc/pa_tnc_msg.h> +#include <bio/bio_writer.h> +#include <bio/bio_reader.h> +#include <debug.h> + +typedef struct private_tcg_pts_attr_dh_nonce_finish_t + private_tcg_pts_attr_dh_nonce_finish_t; + +/** + * PTS DH Nonce Finish + * see section 3.8.3 of PTS Protocol: Binding to TNC IF-M Specification + * + * 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 | Nonce Len | Selected Hash Algorithm | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | D-H Initiator Public Value ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | D-H Initiator Nonce ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +#define PTS_DH_NONCE_FINISH_SIZE 12 +#define PTS_DH_NONCE_FINISH_RESERVED 0x00 + +/** + * Private data of an tcg_pts_attr_dh_nonce_finish_t object. + */ +struct private_tcg_pts_attr_dh_nonce_finish_t { + + /** + * Public members of tcg_pts_attr_dh_nonce_finish_t + */ + tcg_pts_attr_dh_nonce_finish_t public; + + /** + * Attribute vendor ID + */ + pen_t vendor_id; + + /** + * Attribute type + */ + u_int32_t type; + + /** + * Attribute value + */ + chunk_t value; + + /** + * Noskip flag + */ + bool noskip_flag; + + /** + * Selected Hashing Algorithm + */ + pts_meas_algorithms_t hash_algo; + + /** + * DH Initiator Public Value + */ + chunk_t initiator_value; + + /** + * DH Initiator Nonce + */ + chunk_t initiator_nonce; + + /** + * Reference count + */ + refcount_t ref; +}; + +METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->vendor_id; +} + +METHOD(pa_tnc_attr_t, get_type, u_int32_t, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->type; +} + +METHOD(pa_tnc_attr_t, get_value, chunk_t, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->value; +} + +METHOD(pa_tnc_attr_t, get_noskip_flag, bool, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->noskip_flag; +} + +METHOD(pa_tnc_attr_t, set_noskip_flag,void, + private_tcg_pts_attr_dh_nonce_finish_t *this, bool noskip) +{ + this->noskip_flag = noskip; +} + +METHOD(pa_tnc_attr_t, build, void, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + bio_writer_t *writer; + + writer = bio_writer_create(PTS_DH_NONCE_FINISH_SIZE); + writer->write_uint8 (writer, PTS_DH_NONCE_FINISH_RESERVED); + writer->write_uint8 (writer, this->initiator_nonce.len); + writer->write_uint16(writer, this->hash_algo); + writer->write_data (writer, this->initiator_value); + writer->write_data (writer, this->initiator_nonce); + + this->value = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); +} + +METHOD(pa_tnc_attr_t, process, status_t, + private_tcg_pts_attr_dh_nonce_finish_t *this, u_int32_t *offset) +{ + bio_reader_t *reader; + u_int8_t reserved, nonce_len; + u_int16_t hash_algo; + + if (this->value.len < PTS_DH_NONCE_FINISH_SIZE) + { + DBG1(DBG_TNC, "insufficient data for PTS DH Nonce Finish"); + *offset = 0; + return FAILED; + } + reader = bio_reader_create(this->value); + reader->read_uint8 (reader, &reserved); + reader->read_uint8 (reader, &nonce_len); + reader->read_uint16(reader, &hash_algo); + reader->read_data(reader, reader->remaining(reader) - nonce_len, + &this->initiator_value); + reader->read_data(reader, nonce_len, &this->initiator_nonce); + this->hash_algo = hash_algo; + this->initiator_value = chunk_clone(this->initiator_value); + this->initiator_nonce = chunk_clone(this->initiator_nonce); + reader->destroy(reader); + + return SUCCESS; +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + +METHOD(pa_tnc_attr_t, destroy, void, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this->initiator_value.ptr); + free(this->initiator_nonce.ptr); + free(this); + } +} + +METHOD(tcg_pts_attr_dh_nonce_finish_t, get_hash_algo, pts_meas_algorithms_t, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->hash_algo; +} + +METHOD(tcg_pts_attr_dh_nonce_finish_t, get_initiator_value, chunk_t, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->initiator_value; +} + +METHOD(tcg_pts_attr_dh_nonce_finish_t, get_initiator_nonce, chunk_t, + private_tcg_pts_attr_dh_nonce_finish_t *this) +{ + return this->initiator_nonce; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_dh_nonce_finish_create( + pts_meas_algorithms_t hash_algo, + chunk_t initiator_value, + chunk_t initiator_nonce) +{ + private_tcg_pts_attr_dh_nonce_finish_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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_hash_algo = _get_hash_algo, + .get_initiator_nonce = _get_initiator_nonce, + .get_initiator_value = _get_initiator_value, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_DH_NONCE_FINISH, + .hash_algo = hash_algo, + .initiator_value = initiator_value, + .initiator_nonce = chunk_clone(initiator_nonce), + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_dh_nonce_finish_create_from_data(chunk_t value) +{ + private_tcg_pts_attr_dh_nonce_finish_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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_hash_algo = _get_hash_algo, + .get_initiator_nonce = _get_initiator_nonce, + .get_initiator_value = _get_initiator_value, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_DH_NONCE_FINISH, + .value = chunk_clone(value), + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} diff --git a/src/libpts/tcg/tcg_pts_attr_dh_nonce_finish.h b/src/libpts/tcg/tcg_pts_attr_dh_nonce_finish.h new file mode 100644 index 000000000..7148065c5 --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_dh_nonce_finish.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 tcg_pts_attr_dh_nonce_finish tcg_pts_attr_dh_nonce_finish + * @{ @ingroup tcg_pts_attr_dh_nonce_finish + */ + +#ifndef TCG_PTS_ATTR_DH_NONCE_FINISH_H_ +#define TCG_PTS_ATTR_DH_NONCE_FINISH_H_ + +typedef struct tcg_pts_attr_dh_nonce_finish_t tcg_pts_attr_dh_nonce_finish_t; + +#include "tcg_attr.h" +#include "pa_tnc/pa_tnc_attr.h" +#include "pts/pts_meas_algo.h" + +/** + * Class implementing the TCG PTS DH Nonce Finish Attribute + */ +struct tcg_pts_attr_dh_nonce_finish_t { + + /** + * Public PA-TNC attribute interface + */ + pa_tnc_attr_t pa_tnc_attribute; + + /** + * Get nonce length + * + * @return Length of nonce + */ + u_int8_t (*get_nonce_len)(tcg_pts_attr_dh_nonce_finish_t *this); + + /** + * Get selected hash algorithm + * + * @return Selected hash algorithm + */ + pts_meas_algorithms_t (*get_hash_algo)(tcg_pts_attr_dh_nonce_finish_t *this); + + /** + * Get DH Initiator Public Value + * + * @return DH Initiator Public Value + */ + chunk_t (*get_initiator_value)(tcg_pts_attr_dh_nonce_finish_t *this); + + /** + * Get DH Initiator Nonce + * + * @return DH Initiator Nonce + */ + chunk_t (*get_initiator_nonce)(tcg_pts_attr_dh_nonce_finish_t *this); + +}; + +/** + * Creates an tcg_pts_attr_dh_nonce_finish_t object + * + * @param hash_algo Selected hash algorithm + * @param initiator_value DH Initiator Public Value + * @param initiator_nonce DH Initiator Nonce + */ +pa_tnc_attr_t* tcg_pts_attr_dh_nonce_finish_create( + pts_meas_algorithms_t hash_algo, + chunk_t initiator_value, + chunk_t initiator_nonce); + +/** + * Creates an tcg_pts_attr_dh_nonce_finish_t object from received data + * + * @param value unparsed attribute value + */ +pa_tnc_attr_t* tcg_pts_attr_dh_nonce_finish_create_from_data(chunk_t value); + +#endif /** TCG_PTS_ATTR_DH_NONCE_FINISH_H_ @}*/ diff --git a/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_req.c b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_req.c new file mode 100644 index 000000000..36266fe12 --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_req.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "tcg_pts_attr_dh_nonce_params_req.h" + +#include <pa_tnc/pa_tnc_msg.h> +#include <bio/bio_writer.h> +#include <bio/bio_reader.h> +#include <debug.h> + +typedef struct private_tcg_pts_attr_dh_nonce_params_req_t + private_tcg_pts_attr_dh_nonce_params_req_t; + +/** + * PTS DH Nonce Parameters Request + * see section 3.8.1 of PTS Protocol: Binding to TNC IF-M Specification + * + * 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 | Min. Nonce Len | D-H Group Set | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +#define PTS_DH_NONCE_PARAMS_REQ_SIZE 4 +#define PTS_DH_NONCE_PARAMS_REQ_RESERVED 0x00 + +/** + * Private data of an tcg_pts_attr_dh_nonce_params_req_t object. + */ +struct private_tcg_pts_attr_dh_nonce_params_req_t { + + /** + * Public members of tcg_pts_attr_dh_nonce_params_req_t + */ + tcg_pts_attr_dh_nonce_params_req_t public; + + /** + * Attribute vendor ID + */ + pen_t vendor_id; + + /** + * Attribute type + */ + u_int32_t type; + + /** + * Attribute value + */ + chunk_t value; + + /** + * Noskip flag + */ + bool noskip_flag; + + /** + * Minimum acceptable length of nonce + */ + u_int8_t min_nonce_len; + + /** + * Diffie Hellman group set + */ + pts_dh_group_t dh_groups; + + /** + * Reference count + */ + refcount_t ref; +}; + +METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + return this->vendor_id; +} + +METHOD(pa_tnc_attr_t, get_type, u_int32_t, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + return this->type; +} + +METHOD(pa_tnc_attr_t, get_value, chunk_t, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + return this->value; +} + +METHOD(pa_tnc_attr_t, get_noskip_flag, bool, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + return this->noskip_flag; +} + +METHOD(pa_tnc_attr_t, set_noskip_flag,void, + private_tcg_pts_attr_dh_nonce_params_req_t *this, bool noskip) +{ + this->noskip_flag = noskip; +} + +METHOD(pa_tnc_attr_t, build, void, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + bio_writer_t *writer; + + writer = bio_writer_create(PTS_DH_NONCE_PARAMS_REQ_SIZE); + writer->write_uint8 (writer, PTS_DH_NONCE_PARAMS_REQ_RESERVED); + writer->write_uint8 (writer, this->min_nonce_len); + writer->write_uint16(writer, this->dh_groups); + + this->value = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); +} + +METHOD(pa_tnc_attr_t, process, status_t, + private_tcg_pts_attr_dh_nonce_params_req_t *this, u_int32_t *offset) +{ + bio_reader_t *reader; + u_int8_t reserved; + u_int16_t dh_groups; + + if (this->value.len < PTS_DH_NONCE_PARAMS_REQ_SIZE) + { + DBG1(DBG_TNC, "insufficient data for PTS DH Nonce Parameters Request"); + *offset = 0; + return FAILED; + } + reader = bio_reader_create(this->value); + reader->read_uint8(reader, &reserved); + reader->read_uint8(reader, &this->min_nonce_len); + reader->read_uint16(reader, &dh_groups); + this->dh_groups = dh_groups; + reader->destroy(reader); + + return SUCCESS; +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + +METHOD(pa_tnc_attr_t, destroy, void, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this); + } +} + +METHOD(tcg_pts_attr_dh_nonce_params_req_t, get_min_nonce_len, u_int8_t, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + return this->min_nonce_len; +} + +METHOD(tcg_pts_attr_dh_nonce_params_req_t, get_dh_groups, pts_dh_group_t, + private_tcg_pts_attr_dh_nonce_params_req_t *this) +{ + return this->dh_groups; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_dh_nonce_params_req_create(u_int8_t min_nonce_len, + pts_dh_group_t dh_groups) +{ + private_tcg_pts_attr_dh_nonce_params_req_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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_min_nonce_len = _get_min_nonce_len, + .get_dh_groups = _get_dh_groups, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_DH_NONCE_PARAMS_REQ, + .min_nonce_len = min_nonce_len, + .dh_groups = dh_groups, + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_dh_nonce_params_req_create_from_data(chunk_t value) +{ + private_tcg_pts_attr_dh_nonce_params_req_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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_min_nonce_len = _get_min_nonce_len, + .get_dh_groups = _get_dh_groups, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_DH_NONCE_PARAMS_REQ, + .value = chunk_clone(value), + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} diff --git a/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_req.h b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_req.h new file mode 100644 index 000000000..170077156 --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_req.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 tcg_pts_attr_dh_nonce_params_req tcg_pts_attr_dh_nonce_params_req + * @{ @ingroup tcg_pts_attr_dh_nonce_params_req + */ + +#ifndef TCG_PTS_ATTR_DH_NONCE_PARAMS_REQ_H_ +#define TCG_PTS_ATTR_DH_NONCE_PARAMS_REQ_H_ + +typedef struct tcg_pts_attr_dh_nonce_params_req_t + tcg_pts_attr_dh_nonce_params_req_t; + +#include "tcg_attr.h" +#include "pa_tnc/pa_tnc_attr.h" +#include "pts/pts_dh_group.h" + +/** + * Class implementing the TCG PTS DH Nonce Parameters Request Attribute + */ +struct tcg_pts_attr_dh_nonce_params_req_t { + + /** + * Public PA-TNC attribute interface + */ + pa_tnc_attr_t pa_tnc_attribute; + + /** + * Get Minimum nonce length + * + * @return Minimum acceptable length of nonce + */ + u_int8_t (*get_min_nonce_len)(tcg_pts_attr_dh_nonce_params_req_t *this); + + /** + * Get supported Diffie Hellman Groups + * + * @return Supported Diffie Hellman Groups + */ + pts_dh_group_t (*get_dh_groups)(tcg_pts_attr_dh_nonce_params_req_t *this); +}; + +/** + * Creates an tcg_pts_attr_dh_nonce_params_req_t object + * + * @param min_nonce_len Minimum acceptable length of nonce + * @param dh_groups Initiator's supported DH groups + */ +pa_tnc_attr_t* tcg_pts_attr_dh_nonce_params_req_create(u_int8_t min_nonce_len, + pts_dh_group_t dh_groups); + +/** + * Creates an tcg_pts_attr_dh_nonce_params_req_t object from received data + * + * @param value unparsed attribute value + */ +pa_tnc_attr_t* tcg_pts_attr_dh_nonce_params_req_create_from_data(chunk_t value); + +#endif /** TCG_PTS_ATTR_DH_NONCE_PARAMS_REQ_H_ @}*/ diff --git a/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_resp.c b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_resp.c new file mode 100644 index 000000000..09bfa3aac --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_resp.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "tcg_pts_attr_dh_nonce_params_resp.h" + +#include <pa_tnc/pa_tnc_msg.h> +#include <bio/bio_writer.h> +#include <bio/bio_reader.h> +#include <debug.h> + +typedef struct private_tcg_pts_attr_dh_nonce_params_resp_t + private_tcg_pts_attr_dh_nonce_params_resp_t; + +/** + * PTS DH Nonce Parameters Response + * see section 3.8.2 of PTS Protocol: Binding to TNC IF-M Specification + * + * 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 | Nonce Len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Selected D-H Group | Hash Algorithm Set | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | D-H Responder Nonce ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | D-H Responder Public Value ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +#define PTS_DH_NONCE_PARAMS_RESP_SIZE 16 +#define PTS_DH_NONCE_PARAMS_RESP_RESERVED 0x0000 + +/** + * Private data of an tcg_pts_attr_dh_nonce_params_resp_t object. + */ +struct private_tcg_pts_attr_dh_nonce_params_resp_t { + + /** + * Public members of tcg_pts_attr_dh_nonce_params_resp_t + */ + tcg_pts_attr_dh_nonce_params_resp_t public; + + /** + * Attribute vendor ID + */ + pen_t vendor_id; + + /** + * Attribute type + */ + u_int32_t type; + + /** + * Attribute value + */ + chunk_t value; + + /** + * Noskip flag + */ + bool noskip_flag; + + /** + * Selected Diffie Hellman group + */ + pts_dh_group_t dh_group; + + /** + * Supported Hashing Algorithms + */ + pts_meas_algorithms_t hash_algo_set; + + /** + * DH Responder Nonce + */ + chunk_t responder_nonce; + + /** + * DH Responder Public Value + */ + chunk_t responder_value; + + /** + * Reference count + */ + refcount_t ref; +}; + +METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->vendor_id; +} + +METHOD(pa_tnc_attr_t, get_type, u_int32_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->type; +} + +METHOD(pa_tnc_attr_t, get_value, chunk_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->value; +} + +METHOD(pa_tnc_attr_t, get_noskip_flag, bool, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->noskip_flag; +} + +METHOD(pa_tnc_attr_t, set_noskip_flag,void, + private_tcg_pts_attr_dh_nonce_params_resp_t *this, bool noskip) +{ + this->noskip_flag = noskip; +} + +METHOD(pa_tnc_attr_t, build, void, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + bio_writer_t *writer; + + writer = bio_writer_create(PTS_DH_NONCE_PARAMS_RESP_SIZE); + writer->write_uint24(writer, PTS_DH_NONCE_PARAMS_RESP_RESERVED); + writer->write_uint8 (writer, this->responder_nonce.len); + writer->write_uint16(writer, this->dh_group); + writer->write_uint16(writer, this->hash_algo_set); + writer->write_data (writer, this->responder_nonce); + writer->write_data (writer, this->responder_value); + + this->value = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); +} + +METHOD(pa_tnc_attr_t, process, status_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this, u_int32_t *offset) +{ + bio_reader_t *reader; + u_int32_t reserved; + u_int8_t nonce_len; + u_int16_t dh_group, hash_algo_set; + + if (this->value.len < PTS_DH_NONCE_PARAMS_RESP_SIZE) + { + DBG1(DBG_TNC, "insufficient data for PTS DH Nonce Parameters Response"); + *offset = 0; + return FAILED; + } + reader = bio_reader_create(this->value); + reader->read_uint24(reader, &reserved); + reader->read_uint8 (reader, &nonce_len); + reader->read_uint16(reader, &dh_group); + reader->read_uint16(reader, &hash_algo_set); + reader->read_data(reader, nonce_len, &this->responder_nonce); + reader->read_data(reader, reader->remaining(reader), &this->responder_value); + this->dh_group = dh_group; + this->hash_algo_set = hash_algo_set; + this->responder_nonce = chunk_clone(this->responder_nonce); + this->responder_value = chunk_clone(this->responder_value); + reader->destroy(reader); + + return SUCCESS; +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + +METHOD(pa_tnc_attr_t, destroy, void, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this->responder_nonce.ptr); + free(this->responder_value.ptr); + free(this); + } +} + +METHOD(tcg_pts_attr_dh_nonce_params_resp_t, get_dh_group, pts_dh_group_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->dh_group; +} + +METHOD(tcg_pts_attr_dh_nonce_params_resp_t, get_hash_algo_set, + pts_meas_algorithms_t, private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->hash_algo_set; +} + +METHOD(tcg_pts_attr_dh_nonce_params_resp_t, get_responder_nonce, chunk_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->responder_nonce; +} + +METHOD(tcg_pts_attr_dh_nonce_params_resp_t, get_responder_value, chunk_t, + private_tcg_pts_attr_dh_nonce_params_resp_t *this) +{ + return this->responder_value; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_dh_nonce_params_resp_create(pts_dh_group_t dh_group, + pts_meas_algorithms_t hash_algo_set, + chunk_t responder_nonce, + chunk_t responder_value) +{ + private_tcg_pts_attr_dh_nonce_params_resp_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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_dh_group = _get_dh_group, + .get_hash_algo_set = _get_hash_algo_set, + .get_responder_nonce = _get_responder_nonce, + .get_responder_value = _get_responder_value, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_DH_NONCE_PARAMS_RESP, + .dh_group = dh_group, + .hash_algo_set = hash_algo_set, + .responder_nonce = chunk_clone(responder_nonce), + .responder_value = responder_value, + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_dh_nonce_params_resp_create_from_data(chunk_t value) +{ + private_tcg_pts_attr_dh_nonce_params_resp_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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_dh_group = _get_dh_group, + .get_hash_algo_set = _get_hash_algo_set, + .get_responder_nonce = _get_responder_nonce, + .get_responder_value = _get_responder_value, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_DH_NONCE_PARAMS_RESP, + .value = chunk_clone(value), + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} diff --git a/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_resp.h b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_resp.h new file mode 100644 index 000000000..d2141f8b9 --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_dh_nonce_params_resp.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 tcg_pts_attr_dh_nonce_params_resp tcg_pts_attr_dh_nonce_params_resp + * @{ @ingroup tcg_pts_attr_dh_nonce_params_resp + */ + +#ifndef TCG_PTS_ATTR_DH_NONCE_PARAMS_RESP_H_ +#define TCG_PTS_ATTR_DH_NONCE_PARAMS_RESP_H_ + +typedef struct tcg_pts_attr_dh_nonce_params_resp_t + tcg_pts_attr_dh_nonce_params_resp_t; + +#include "tcg_attr.h" +#include "pa_tnc/pa_tnc_attr.h" +#include "pts/pts_dh_group.h" +#include "pts/pts_meas_algo.h" + +/** + * Class implementing the TCG PTS DH Nonce Parameters Response Attribute + */ +struct tcg_pts_attr_dh_nonce_params_resp_t { + + /** + * Public PA-TNC attribute interface + */ + pa_tnc_attr_t pa_tnc_attribute; + + /** + * Get selected Diffie Hellman Group + * + * @return Selected Diffie Hellman Group + */ + pts_dh_group_t (*get_dh_group)(tcg_pts_attr_dh_nonce_params_resp_t *this); + + /** + * Get supported hash algorithms + * + * @return Hash algorithm set + */ + pts_meas_algorithms_t (*get_hash_algo_set)( + tcg_pts_attr_dh_nonce_params_resp_t *this); + + /** + * Get DH Responder Nonce + * + * @return DH Responder Nonce + */ + chunk_t (*get_responder_nonce)(tcg_pts_attr_dh_nonce_params_resp_t *this); + + /** + * Get DH Responder Public Value + * + * @return DH Responder Public Value + */ + chunk_t (*get_responder_value)(tcg_pts_attr_dh_nonce_params_resp_t *this); + +}; + +/** + * Creates an tcg_pts_attr_dh_nonce_params_resp_t object + * + * @param dh_group Selected DH group + * @param hash_algo_set Set of supported hash algorithms + * @param responder_nonce DH Responder Nonce + * @param responder_pub_val DH Responder Public value + */ +pa_tnc_attr_t* tcg_pts_attr_dh_nonce_params_resp_create(pts_dh_group_t dh_group, + pts_meas_algorithms_t hash_algo_set, + chunk_t responder_nonce, + chunk_t responder_value); + +/** + * Creates an tcg_pts_attr_dh_nonce_params_resp_t object from received data + * + * @param value unparsed attribute value + */ +pa_tnc_attr_t* tcg_pts_attr_dh_nonce_params_resp_create_from_data(chunk_t value); + +#endif /** TCG_PTS_ATTR_DH_NONCE_PARAMS_RESP_H_ @}*/ diff --git a/src/libpts/tcg/tcg_pts_attr_file_meas.c b/src/libpts/tcg/tcg_pts_attr_file_meas.c index fe559d004..737da65c1 100644 --- a/src/libpts/tcg/tcg_pts_attr_file_meas.c +++ b/src/libpts/tcg/tcg_pts_attr_file_meas.c @@ -88,6 +88,10 @@ struct private_tcg_pts_attr_file_meas_t { */ pts_file_meas_t *measurements; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -133,11 +137,9 @@ METHOD(pa_tnc_attr_t, build, void, number_of_files = this->measurements->get_file_count(this->measurements); request_id = this->measurements->get_request_id(this->measurements); - writer = bio_writer_create(PTS_FILE_MEAS_SIZE); - /* Write the 64 bit integer as two 32 bit parts */ - writer->write_uint32(writer, number_of_files >> 32); - writer->write_uint32(writer, number_of_files & 0xffffffff); + writer = bio_writer_create(PTS_FILE_MEAS_SIZE); + writer->write_uint64(writer, number_of_files); writer->write_uint16(writer, request_id); enumerator = this->measurements->create_enumerator(this->measurements); @@ -168,8 +170,7 @@ METHOD(pa_tnc_attr_t, process, status_t, private_tcg_pts_attr_file_meas_t *this, u_int32_t *offset) { bio_reader_t *reader; - int count; - u_int32_t number_of_files; + u_int64_t number_of_files; u_int16_t request_id, meas_len, filename_len; size_t len; chunk_t measurement, filename; @@ -182,18 +183,15 @@ METHOD(pa_tnc_attr_t, process, status_t, *offset = 0; return FAILED; } - reader = bio_reader_create(this->value); - reader->read_uint32(reader, &number_of_files); - count = (sizeof(count) > 4) ? number_of_files << 32 : 0; - reader->read_uint32(reader, &number_of_files); - count += number_of_files; + reader = bio_reader_create(this->value); + reader->read_uint64(reader, &number_of_files); reader->read_uint16(reader, &request_id); reader->read_uint16(reader, &meas_len); this->measurements = pts_file_meas_create(request_id); - while (count--) + while (number_of_files--) { if (!reader->read_data(reader, meas_len, &measurement)) { @@ -223,12 +221,21 @@ end: return status; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_file_meas_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_file_meas_t *this) { - this->measurements->destroy(this->measurements); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + this->measurements->destroy(this->measurements); + free(this->value.ptr); + free(this); + } } METHOD(tcg_pts_attr_file_meas_t, get_measurements, pts_file_meas_t*, @@ -254,6 +261,7 @@ pa_tnc_attr_t *tcg_pts_attr_file_meas_create(pts_file_meas_t *measurements) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_measurements = _get_measurements, @@ -261,6 +269,7 @@ pa_tnc_attr_t *tcg_pts_attr_file_meas_create(pts_file_meas_t *measurements) .vendor_id = PEN_TCG, .type = TCG_PTS_FILE_MEAS, .measurements = measurements, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -284,6 +293,7 @@ pa_tnc_attr_t *tcg_pts_attr_file_meas_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_measurements = _get_measurements, @@ -291,6 +301,7 @@ pa_tnc_attr_t *tcg_pts_attr_file_meas_create_from_data(chunk_t data) .vendor_id = PEN_TCG, .type = TCG_PTS_FILE_MEAS, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_gen_attest_evid.c b/src/libpts/tcg/tcg_pts_attr_gen_attest_evid.c index 4d8aa7bee..054285c4e 100644 --- a/src/libpts/tcg/tcg_pts_attr_gen_attest_evid.c +++ b/src/libpts/tcg/tcg_pts_attr_gen_attest_evid.c @@ -20,7 +20,8 @@ #include <bio/bio_reader.h> #include <debug.h> -typedef struct private_tcg_pts_attr_gen_attest_evid_t private_tcg_pts_attr_gen_attest_evid_t; +typedef struct private_tcg_pts_attr_gen_attest_evid_t + private_tcg_pts_attr_gen_attest_evid_t; /** * Generate Attestation Evidence @@ -67,6 +68,11 @@ struct private_tcg_pts_attr_gen_attest_evid_t { * Noskip flag */ bool noskip_flag; + + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -130,11 +136,21 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_gen_attest_evid_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_gen_attest_evid_t *this) { - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this); + } } /** @@ -154,11 +170,13 @@ pa_tnc_attr_t *tcg_pts_attr_gen_attest_evid_create() .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, }, .vendor_id = PEN_TCG, .type = TCG_PTS_GEN_ATTEST_EVID, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -182,12 +200,14 @@ pa_tnc_attr_t *tcg_pts_attr_gen_attest_evid_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, }, .vendor_id = PEN_TCG, .type = TCG_PTS_GEN_ATTEST_EVID, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_get_aik.c b/src/libpts/tcg/tcg_pts_attr_get_aik.c index 727c7a211..1875375a4 100644 --- a/src/libpts/tcg/tcg_pts_attr_get_aik.c +++ b/src/libpts/tcg/tcg_pts_attr_get_aik.c @@ -65,6 +65,11 @@ struct private_tcg_pts_attr_get_aik_t { * Noskip flag */ bool noskip_flag; + + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -128,11 +133,21 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_get_aik_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_get_aik_t *this) { - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this); + } } /** @@ -152,11 +167,13 @@ pa_tnc_attr_t *tcg_pts_attr_get_aik_create() .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, }, .vendor_id = PEN_TCG, .type = TCG_PTS_GET_AIK, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -180,12 +197,14 @@ pa_tnc_attr_t *tcg_pts_attr_get_aik_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, }, .vendor_id = PEN_TCG, .type = TCG_PTS_GET_AIK, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.c b/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.c index 51cb99a8e..cb6834ca5 100644 --- a/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.c +++ b/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.c @@ -20,7 +20,8 @@ #include <bio/bio_reader.h> #include <debug.h> -typedef struct private_tcg_pts_attr_get_tpm_version_info_t private_tcg_pts_attr_get_tpm_version_info_t; +typedef struct private_tcg_pts_attr_get_tpm_version_info_t + private_tcg_pts_attr_get_tpm_version_info_t; /** * Get TPM Version Information @@ -67,6 +68,11 @@ struct private_tcg_pts_attr_get_tpm_version_info_t { * Noskip flag */ bool noskip_flag; + + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -130,11 +136,21 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_get_tpm_version_info_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_get_tpm_version_info_t *this) { - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this); + } } /** @@ -154,11 +170,13 @@ pa_tnc_attr_t *tcg_pts_attr_get_tpm_version_info_create() .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, }, .vendor_id = PEN_TCG, .type = TCG_PTS_GET_TPM_VERSION_INFO, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -182,12 +200,14 @@ pa_tnc_attr_t *tcg_pts_attr_get_tpm_version_info_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, }, .vendor_id = PEN_TCG, .type = TCG_PTS_GET_TPM_VERSION_INFO, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.h b/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.h index 255efaafa..1b693402a 100644 --- a/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.h +++ b/src/libpts/tcg/tcg_pts_attr_get_tpm_version_info.h @@ -21,7 +21,8 @@ #ifndef TCG_PTS_ATTR_GET_TPM_VERSION_INFO_H_ #define TCG_PTS_ATTR_GET_TPM_VERSION_INFO_H_ -typedef struct tcg_pts_attr_get_tpm_version_info_t tcg_pts_attr_get_tpm_version_info_t; +typedef struct tcg_pts_attr_get_tpm_version_info_t + tcg_pts_attr_get_tpm_version_info_t; #include "tcg_attr.h" #include "pa_tnc/pa_tnc_attr.h" diff --git a/src/libpts/tcg/tcg_pts_attr_meas_algo.c b/src/libpts/tcg/tcg_pts_attr_meas_algo.c index dffc15320..ed520e3cd 100644 --- a/src/libpts/tcg/tcg_pts_attr_meas_algo.c +++ b/src/libpts/tcg/tcg_pts_attr_meas_algo.c @@ -72,6 +72,10 @@ struct private_tcg_pts_attr_meas_algo_t { */ pts_meas_algorithms_t algorithms; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -140,8 +144,18 @@ METHOD(pa_tnc_attr_t, process, status_t, METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_meas_algo_t *this) { - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this); + } +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_meas_algo_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; } METHOD(tcg_pts_attr_meas_algo_t, get_algorithms, pts_meas_algorithms_t, @@ -168,6 +182,7 @@ pa_tnc_attr_t *tcg_pts_attr_meas_algo_create(pts_meas_algorithms_t algorithms, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_algorithms = _get_algorithms, @@ -175,6 +190,7 @@ pa_tnc_attr_t *tcg_pts_attr_meas_algo_create(pts_meas_algorithms_t algorithms, .vendor_id = PEN_TCG, .type = selection ? TCG_PTS_MEAS_ALGO_SELECTION : TCG_PTS_MEAS_ALGO, .algorithms = algorithms, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -199,6 +215,7 @@ pa_tnc_attr_t *tcg_pts_attr_meas_algo_create_from_data(chunk_t data, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_algorithms = _get_algorithms, @@ -206,6 +223,7 @@ pa_tnc_attr_t *tcg_pts_attr_meas_algo_create_from_data(chunk_t data, .vendor_id = PEN_TCG, .type = selection ? TCG_PTS_MEAS_ALGO_SELECTION : TCG_PTS_MEAS_ALGO, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_proto_caps.c b/src/libpts/tcg/tcg_pts_attr_proto_caps.c index 6d078905d..055c750ff 100644 --- a/src/libpts/tcg/tcg_pts_attr_proto_caps.c +++ b/src/libpts/tcg/tcg_pts_attr_proto_caps.c @@ -72,6 +72,10 @@ struct private_tcg_pts_attr_proto_caps_t { */ pts_proto_caps_flag_t flags; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -141,8 +145,18 @@ METHOD(pa_tnc_attr_t, process, status_t, METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_proto_caps_t *this) { - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this); + } +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_proto_caps_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; } METHOD(tcg_pts_attr_proto_caps_t, get_flags, pts_proto_caps_flag_t, @@ -169,6 +183,7 @@ pa_tnc_attr_t *tcg_pts_attr_proto_caps_create(pts_proto_caps_flag_t flags, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_flags = _get_flags, @@ -176,6 +191,7 @@ pa_tnc_attr_t *tcg_pts_attr_proto_caps_create(pts_proto_caps_flag_t flags, .vendor_id = PEN_TCG, .type = request ? TCG_PTS_REQ_PROTO_CAPS : TCG_PTS_PROTO_CAPS, .flags = flags, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -199,6 +215,7 @@ pa_tnc_attr_t *tcg_pts_attr_proto_caps_create_from_data(chunk_t data, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_flags = _get_flags, @@ -206,6 +223,7 @@ pa_tnc_attr_t *tcg_pts_attr_proto_caps_create_from_data(chunk_t data, .vendor_id = PEN_TCG, .type = request ? TCG_PTS_REQ_PROTO_CAPS : TCG_PTS_PROTO_CAPS, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_req_file_meas.c b/src/libpts/tcg/tcg_pts_attr_req_file_meas.c index 68ecfa8f1..17781f745 100644 --- a/src/libpts/tcg/tcg_pts_attr_req_file_meas.c +++ b/src/libpts/tcg/tcg_pts_attr_req_file_meas.c @@ -93,6 +93,10 @@ struct private_tcg_pts_attr_req_file_meas_t { */ char *pathname; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -181,12 +185,22 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_req_file_meas_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_req_file_meas_t *this) { - free(this->pathname); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->pathname); + free(this->value.ptr); + free(this); + } } METHOD(tcg_pts_attr_req_file_meas_t, get_directory_flag, bool, @@ -233,6 +247,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create(bool directory_flag, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_directory_flag = _get_directory_flag, @@ -246,6 +261,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create(bool directory_flag, .request_id = request_id, .delimiter = delimiter, .pathname = strdup(pathname), + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -269,6 +285,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_directory_flag = _get_directory_flag, @@ -279,6 +296,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create_from_data(chunk_t data) .vendor_id = PEN_TCG, .type = TCG_PTS_REQ_FILE_MEAS, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_req_file_meta.c b/src/libpts/tcg/tcg_pts_attr_req_file_meta.c index f42903e03..bef6b5db6 100644 --- a/src/libpts/tcg/tcg_pts_attr_req_file_meta.c +++ b/src/libpts/tcg/tcg_pts_attr_req_file_meta.c @@ -86,6 +86,10 @@ struct private_tcg_pts_attr_req_file_meta_t { */ char *pathname; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -177,9 +181,19 @@ METHOD(pa_tnc_attr_t, process, status_t, METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_req_file_meta_t *this) { - free(this->pathname); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->pathname); + free(this->value.ptr); + free(this); + } +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_req_file_meta_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; } METHOD(tcg_pts_attr_req_file_meta_t, get_directory_flag, bool, @@ -188,7 +202,7 @@ METHOD(tcg_pts_attr_req_file_meta_t, get_directory_flag, bool, return this->directory_flag; } -METHOD(tcg_pts_attr_req_file_meta_t, get_delimiter, u_int32_t, +METHOD(tcg_pts_attr_req_file_meta_t, get_delimiter, u_int8_t, private_tcg_pts_attr_req_file_meta_t *this) { return this->delimiter; @@ -203,7 +217,7 @@ METHOD(tcg_pts_attr_req_file_meta_t, get_pathname, char*, /** * Described in header. */ -pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create(bool directory_flag, +pa_tnc_attr_t *tcg_pts_attr_req_file_meta_create(bool directory_flag, u_int8_t delimiter, char *pathname) { @@ -219,6 +233,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create(bool directory_flag, .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_directory_flag = _get_directory_flag, @@ -230,6 +245,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create(bool directory_flag, .directory_flag = directory_flag, .delimiter = delimiter, .pathname = strdup(pathname), + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -239,7 +255,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create(bool directory_flag, /** * Described in header. */ -pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create_from_data(chunk_t data) +pa_tnc_attr_t *tcg_pts_attr_req_file_meta_create_from_data(chunk_t data) { private_tcg_pts_attr_req_file_meta_t *this; @@ -253,6 +269,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_directory_flag = _get_directory_flag, @@ -262,6 +279,7 @@ pa_tnc_attr_t *tcg_pts_attr_req_file_meas_create_from_data(chunk_t data) .vendor_id = PEN_TCG, .type = TCG_PTS_REQ_FILE_META, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_req_func_comp_evid.c b/src/libpts/tcg/tcg_pts_attr_req_func_comp_evid.c new file mode 100644 index 000000000..bfd108b9f --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_req_func_comp_evid.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 "tcg_pts_attr_req_func_comp_evid.h" + +#include <pa_tnc/pa_tnc_msg.h> +#include <bio/bio_writer.h> +#include <bio/bio_reader.h> +#include <utils/linked_list.h> +#include <debug.h> + +typedef struct private_tcg_pts_attr_req_func_comp_evid_t private_tcg_pts_attr_req_func_comp_evid_t; + +/** + * Request Functional Component Evidence + * see section 3.14.1 of PTS Protocol: Binding to TNC IF-M Specification + * + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Flags | Sub-component Depth (for Component #1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Component Functional Name #1 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Component Functional Name #1 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ........ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Flags | Sub-component Depth (for Component #N) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Component Functional Name #N | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Component Functional Name #N | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/** + * Component Functional Name Structure + * (see section 5.1 of PTS Protocol: Binding to TNC IF-M Specification) + * + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Component Functional Name Vendor ID |Fam| Qualifier | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Component Functional Name | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +#define PTS_REQ_FUNC_COMP_EVID_SIZE 12 +#define PTS_REQ_FUNC_COMP_FAMILY_MASK 0xC0 + +/** + * Private data of an tcg_pts_attr_req_func_comp_evid_t object. + */ +struct private_tcg_pts_attr_req_func_comp_evid_t { + + /** + * Public members of tcg_pts_attr_req_func_comp_evid_t + */ + tcg_pts_attr_req_func_comp_evid_t public; + + /** + * Attribute vendor ID + */ + pen_t vendor_id; + + /** + * Attribute type + */ + u_int32_t type; + + /** + * Attribute value + */ + chunk_t value; + + /** + * Noskip flag + */ + bool noskip_flag; + + /** + * List of Functional Components + */ + linked_list_t *list; + + /** + * Reference count + */ + refcount_t ref; +}; + +typedef struct entry_t entry_t; + +/** + * Functional component entry + */ +struct entry_t { + u_int8_t flags; + u_int32_t depth; + pts_comp_func_name_t *name; +}; + +/** + * Enumerate functional component entries + */ +static bool entry_filter(void *null, entry_t **entry, u_int8_t *flags, + void *i2, u_int32_t *depth, void *i3, + pts_comp_func_name_t **name) +{ + *flags = (*entry)->flags; + *depth = (*entry)->depth; + *name = (*entry)->name; + + return TRUE; +} + +/** + * Free an entry_t object + */ +static void free_entry(entry_t *this) +{ + if (this) + { + this->name->destroy(this->name); + free(this); + } +} + +METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + return this->vendor_id; +} + +METHOD(pa_tnc_attr_t, get_type, u_int32_t, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + return this->type; +} + +METHOD(pa_tnc_attr_t, get_value, chunk_t, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + return this->value; +} + +METHOD(pa_tnc_attr_t, get_noskip_flag, bool, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + return this->noskip_flag; +} + +METHOD(pa_tnc_attr_t, set_noskip_flag,void, + private_tcg_pts_attr_req_func_comp_evid_t *this, bool noskip) +{ + this->noskip_flag = noskip; +} + +METHOD(pa_tnc_attr_t, build, void, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + bio_writer_t *writer; + enumerator_t *enumerator; + entry_t *entry; + + writer = bio_writer_create(PTS_REQ_FUNC_COMP_EVID_SIZE); + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + writer->write_uint8 (writer, entry->flags); + writer->write_uint24(writer, entry->depth); + writer->write_uint24(writer, entry->name->get_vendor_id(entry->name)); + writer->write_uint8 (writer, entry->name->get_qualifier(entry->name)); + writer->write_uint32(writer, entry->name->get_name(entry->name)); + } + enumerator->destroy(enumerator); + + this->value = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); +} + +METHOD(pa_tnc_attr_t, process, status_t, + private_tcg_pts_attr_req_func_comp_evid_t *this, u_int32_t *offset) +{ + bio_reader_t *reader; + u_int32_t depth, vendor_id, name; + u_int8_t flags, fam_and_qualifier, qualifier; + status_t status = FAILED; + entry_t *entry = NULL; + + if (this->value.len < PTS_REQ_FUNC_COMP_EVID_SIZE) + { + DBG1(DBG_TNC, "insufficient data for Request Functional " + "Component Evidence"); + *offset = 0; + return FAILED; + } + reader = bio_reader_create(this->value); + + while (reader->remaining(reader)) + { + if (!reader->read_uint8(reader, &flags)) + { + DBG1(DBG_TNC, "insufficient data for PTS Request Functional " + "Component Evidence Flags"); + goto end; + } + if (!reader->read_uint24(reader, &depth)) + { + DBG1(DBG_TNC, "insufficient data for PTS Request Functional " + "Component Evidence Sub Component Depth"); + goto end; + } + if (!reader->read_uint24(reader, &vendor_id)) + { + DBG1(DBG_TNC, "insufficient data for PTS Request Functional " + "Component Evidence Component Name Vendor ID"); + goto end; + } + if (!reader->read_uint8(reader, &fam_and_qualifier)) + { + DBG1(DBG_TNC, "insufficient data for PTS Request Functional " + "Component Evidence Family and Qualifier"); + goto end; + } + if (fam_and_qualifier & PTS_REQ_FUNC_COMP_FAMILY_MASK) + { + DBG1(DBG_TNC, "the Functional Name Encoding Family " + "is not Binary Enumeration"); + goto end; + } + if (!reader->read_uint32(reader, &name)) + { + DBG1(DBG_TNC, "insufficient data for PTS Request Functional " + "Component Evidence Component Functional Name"); + goto end; + } + qualifier = fam_and_qualifier & ~PTS_REQ_FUNC_COMP_FAMILY_MASK; + + entry = malloc_thing(entry_t); + entry->flags = flags; + entry->depth = depth; + entry->name = pts_comp_func_name_create(vendor_id, name, qualifier); + + this->list->insert_last(this->list, entry); + } + status = SUCCESS; + +end: + reader->destroy(reader); + return status; +} + +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + +METHOD(pa_tnc_attr_t, destroy, void, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + if (ref_put(&this->ref)) + { + this->list->destroy_function(this->list, (void *)free_entry); + free(this->value.ptr); + free(this); + } +} + +METHOD(tcg_pts_attr_req_func_comp_evid_t, add_component, void, + private_tcg_pts_attr_req_func_comp_evid_t *this, u_int8_t flags, + u_int32_t depth, pts_comp_func_name_t *name) +{ + entry_t *entry; + + entry = malloc_thing(entry_t); + entry->flags = flags; + entry->depth = depth; + entry->name = name; + this->list->insert_last(this->list, entry); +} + +METHOD(tcg_pts_attr_req_func_comp_evid_t, get_count, int, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + return this->list->get_count(this->list); +} + +METHOD(tcg_pts_attr_req_func_comp_evid_t, create_enumerator, enumerator_t*, + private_tcg_pts_attr_req_func_comp_evid_t *this) +{ + return enumerator_create_filter(this->list->create_enumerator(this->list), + (void*)entry_filter, NULL, NULL); +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_req_func_comp_evid_create(void) +{ + private_tcg_pts_attr_req_func_comp_evid_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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, + }, + .add_component = _add_component, + .get_count = _get_count, + .create_enumerator = _create_enumerator, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_REQ_FUNC_COMP_EVID, + .list = linked_list_create(), + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} + +/** + * Described in header. + */ +pa_tnc_attr_t *tcg_pts_attr_req_func_comp_evid_create_from_data(chunk_t data) +{ + private_tcg_pts_attr_req_func_comp_evid_t *this; + + INIT(this, + .public = { + .pa_tnc_attribute = { + .get_vendor_id = _get_vendor_id, + .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, + }, + .add_component = _add_component, + .get_count = _get_count, + .create_enumerator = _create_enumerator, + }, + .vendor_id = PEN_TCG, + .type = TCG_PTS_REQ_FUNC_COMP_EVID, + .list = linked_list_create(), + .value = chunk_clone(data), + .ref = 1, + ); + + return &this->public.pa_tnc_attribute; +} diff --git a/src/libpts/tcg/tcg_pts_attr_req_func_comp_evid.h b/src/libpts/tcg/tcg_pts_attr_req_func_comp_evid.h new file mode 100644 index 000000000..031955aca --- /dev/null +++ b/src/libpts/tcg/tcg_pts_attr_req_func_comp_evid.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Sansar Choinyambuu + * 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 tcg_pts_attr_req_func_comp_evid tcg_pts_attr_req_func_comp_evid + * @{ @ingroup tcg_pts_attr_req_func_comp_evid + */ + +#ifndef TCG_PTS_ATTR_REQ_FUNC_COMP_EVID_H_ +#define TCG_PTS_ATTR_REQ_FUNC_COMP_EVID_H_ + +typedef struct tcg_pts_attr_req_func_comp_evid_t tcg_pts_attr_req_func_comp_evid_t; + +#include "tcg_attr.h" +#include "pts/components/pts_comp_func_name.h" +#include "pa_tnc/pa_tnc_attr.h" + +/** + * Class implementing the TCG PTS Request Functional Component Evidence attribute + * + */ +struct tcg_pts_attr_req_func_comp_evid_t { + + /** + * Public PA-TNC attribute interface + */ + pa_tnc_attr_t pa_tnc_attribute; + + /** + * Add a component to the Functional Component Evidence Request + * + * @param flags Component Evidence Request Flags + * @param depth Sub-component Depth + * @param name Functional Component Name + */ + void (*add_component)(tcg_pts_attr_req_func_comp_evid_t *this, + u_int8_t flags, u_int32_t depth, + pts_comp_func_name_t *name); + + /** + * Returns the number of Functional Component entries + * + * @return Number of entries + */ + int (*get_count)(tcg_pts_attr_req_func_comp_evid_t *this); + + /** + * Enumerator over Functional Component entries + * + * @return Entry enumerator + */ + enumerator_t* (*create_enumerator)(tcg_pts_attr_req_func_comp_evid_t *this); + +}; + +/** + * Creates a tcg_pts_attr_req_func_comp_evid_t object + */ +pa_tnc_attr_t* tcg_pts_attr_req_func_comp_evid_create(void); + +/** + * Creates a tcg_pts_attr_req_func_comp_evid_t object from received data + * + * @param value Unparsed attribute value + */ +pa_tnc_attr_t* tcg_pts_attr_req_func_comp_evid_create_from_data(chunk_t value); + +#endif /** TCG_PTS_ATTR_REQ_FUNC_COMP_EVID_H_ @}*/ diff --git a/src/libpts/tcg/tcg_pts_attr_req_funct_comp_evid.c b/src/libpts/tcg/tcg_pts_attr_req_funct_comp_evid.c deleted file mode 100644 index 0f460580b..000000000 --- a/src/libpts/tcg/tcg_pts_attr_req_funct_comp_evid.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (C) 2011 Sansar Choinyambuu - * 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 "tcg_pts_attr_req_funct_comp_evid.h" - -#include <pa_tnc/pa_tnc_msg.h> -#include <bio/bio_writer.h> -#include <bio/bio_reader.h> -#include <debug.h> - -typedef struct private_tcg_pts_attr_req_funct_comp_evid_t private_tcg_pts_attr_req_funct_comp_evid_t; - -/** - * Request Functional Component Evidence - * see section 3.14.1 of PTS Protocol: Binding to TNC IF-M Specification - * - * 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 - * - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Flags | Sub-component Depth | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Component Functional Name | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - */ - -/** - * Component Functional Name Structure (see section 5.1 of PTS Protocol: Binding to TNC IF-M Specification) - * - * 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 - * - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Component Functional Name Vendor ID |Fam| Qualifier | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Component Functional Name | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - */ - -/** - * Qualifier for Functional Component - * see section 5.2 of PTS Protocol: Binding to TNC IF-M Specification - * - * - * 0 1 2 3 4 5 - * +-+-+-+-+-+-+ - * |K|S| Type | - * +-+-+-+-+-+-+ - */ - -#define PTS_REQ_FUNCT_COMP_EVID_SIZE 12 -#define PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM 0x00 - -/** - * Private data of an tcg_pts_attr_req_funct_comp_evid_t object. - */ -struct private_tcg_pts_attr_req_funct_comp_evid_t { - - /** - * Public members of tcg_pts_attr_req_funct_comp_evid_t - */ - tcg_pts_attr_req_funct_comp_evid_t public; - - /** - * Attribute vendor ID - */ - pen_t vendor_id; - - /** - * Attribute type - */ - u_int32_t type; - - /** - * Attribute value - */ - chunk_t value; - - /** - * Noskip flag - */ - bool noskip_flag; - - /** - * Set of flags for Request Functional Component - */ - pts_attr_req_funct_comp_evid_flag_t flags; - - /** - * Sub-component Depth - */ - u_int32_t depth; - - /** - * Component Functional Name Vendor ID - */ - u_int32_t comp_vendor_id; - - /** - * Functional Name Encoding Family - */ - u_int8_t family; - - /** - * Functional Name Category Qualifier - */ - pts_qualifier_t qualifier; - - /** - * Component Functional Name - */ - pts_funct_comp_name_t name; -}; - -METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->vendor_id; -} - -METHOD(pa_tnc_attr_t, get_type, u_int32_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->type; -} - -METHOD(pa_tnc_attr_t, get_value, chunk_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->value; -} - -METHOD(pa_tnc_attr_t, get_noskip_flag, bool, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->noskip_flag; -} - -METHOD(pa_tnc_attr_t, set_noskip_flag,void, - private_tcg_pts_attr_req_funct_comp_evid_t *this, bool noskip) -{ - this->noskip_flag = noskip; -} - -METHOD(pa_tnc_attr_t, build, void, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - bio_writer_t *writer; - u_int8_t flags = 0; - u_int8_t qualifier = 0; - - writer = bio_writer_create(PTS_REQ_FUNCT_COMP_EVID_SIZE); - - /* Determine the flags to set*/ - if (this->flags & PTS_REQ_FUNC_COMP_FLAG_PCR) - { - flags += 128; - } - if (this->flags & PTS_REQ_FUNC_COMP_FLAG_CURR) - { - flags += 64; - } - if (this->flags & PTS_REQ_FUNC_COMP_FLAG_VER) - { - flags += 32; - } - if (this->flags & PTS_REQ_FUNC_COMP_FLAG_TTC) - { - flags += 16; - } - writer->write_uint8(writer, flags); - - writer->write_uint24 (writer, this->depth); - writer->write_uint24 (writer, this->comp_vendor_id); - - if (this->family != PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM) - { - DBG1(DBG_TNC, "Functional Name Encoding Family is not set to 00"); - } - - qualifier += this->qualifier.type; - if (this->qualifier.kernel) - { - qualifier += 16; - } - if (this->qualifier.sub_component) - { - qualifier += 32; - } - writer->write_uint8 (writer, qualifier); - writer->write_uint32 (writer, this->name); - - this->value = chunk_clone(writer->get_buf(writer)); - writer->destroy(writer); -} - -METHOD(pa_tnc_attr_t, process, status_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this, u_int32_t *offset) -{ - bio_reader_t *reader; - u_int8_t flags; - u_int8_t fam_and_qualifier; - - if (this->value.len < PTS_REQ_FUNCT_COMP_EVID_SIZE) - { - DBG1(DBG_TNC, "insufficient data for Request Functional Component Evidence"); - *offset = 0; - return FAILED; - } - reader = bio_reader_create(this->value); - - reader->read_uint8(reader, &flags); - if ((flags >> 4) & 1) - { - this->flags |= PTS_REQ_FUNC_COMP_FLAG_PCR; - } - if ((flags >> 5) & 1) - { - this->flags |= PTS_REQ_FUNC_COMP_FLAG_CURR; - } - if ((flags >> 6) & 1) - { - this->flags |= PTS_REQ_FUNC_COMP_FLAG_VER; - } - if ((flags >> 7) & 1) - { - this->flags |= PTS_REQ_FUNC_COMP_FLAG_TTC; - } - - reader->read_uint24(reader, &this->depth); - reader->read_uint24(reader, &this->comp_vendor_id); - reader->read_uint8(reader, &fam_and_qualifier); - - if (((fam_and_qualifier >> 6) & 1) ) - { - this->family += 1; - } - if (((fam_and_qualifier >> 7) & 1) ) - { - this->family += 2; - } - - /* TODO: Generate an IF-M error attribute indicating */ - /* TCG_PTS_INVALID_NAME_FAM */ - //if (&this->comp_vendor_id==PEN_TCG && this->family != PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM) - //{ - // DBG1(DBG_TNC, "Functional Name Encoding Family is not set to 00"); - //} - - if (((fam_and_qualifier >> 5) & 1) ) - { - this->qualifier.kernel = true; - } - if (((fam_and_qualifier >> 4) & 1) ) - { - this->qualifier.sub_component = true; - } - this->qualifier.type = ( fam_and_qualifier & 0xF ); - /* TODO: Check the type is defined in pts_attr_req_funct_comp_type_t */ - - reader->read_uint32(reader, &this->name); - /* TODO: Check the name is defined in pts_funct_comp_name_t */ - - reader->destroy(reader); - return SUCCESS; -} - -METHOD(pa_tnc_attr_t, destroy, void, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - free(this->value.ptr); - free(this); -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, get_flags, pts_attr_req_funct_comp_evid_flag_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->flags; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, set_flags, void, - private_tcg_pts_attr_req_funct_comp_evid_t *this, pts_attr_req_funct_comp_evid_flag_t flags) -{ - this->flags = flags; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, get_sub_component_depth, u_int32_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->depth; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, get_comp_funct_name_vendor_id, u_int32_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->comp_vendor_id; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, get_family, u_int8_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->family; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, get_qualifier, pts_qualifier_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->qualifier; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, set_qualifier, void, - private_tcg_pts_attr_req_funct_comp_evid_t *this, pts_qualifier_t qualifier) -{ - this->qualifier = qualifier; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, get_comp_funct_name, pts_funct_comp_name_t, - private_tcg_pts_attr_req_funct_comp_evid_t *this) -{ - return this->name; -} - -METHOD(tcg_pts_attr_req_funct_comp_evid_t, set_comp_funct_name, void, - private_tcg_pts_attr_req_funct_comp_evid_t *this, pts_funct_comp_name_t name) -{ - this->name = name; -} - -/** - * Described in header. - */ -pa_tnc_attr_t *tcg_pts_attr_req_funct_comp_evid_create( - pts_attr_req_funct_comp_evid_flag_t flags, - u_int32_t depth, u_int32_t vendor_id, - pts_qualifier_t qualifier, - pts_funct_comp_name_t name) -{ - private_tcg_pts_attr_req_funct_comp_evid_t *this; - - INIT(this, - .public = { - .pa_tnc_attribute = { - .get_vendor_id = _get_vendor_id, - .get_type = _get_type, - .get_value = _get_value, - .get_noskip_flag = _get_noskip_flag, - .set_noskip_flag = _set_noskip_flag, - .build = _build, - .process = _process, - .destroy = _destroy, - }, - .get_flags= _get_flags, - .set_flags= _set_flags, - .get_sub_component_depth = _get_sub_component_depth, - .get_comp_funct_name_vendor_id = _get_comp_funct_name_vendor_id, - .get_family = _get_family, - .get_qualifier = _get_qualifier, - .set_qualifier = _set_qualifier, - .get_comp_funct_name = _get_comp_funct_name, - .set_comp_funct_name = _set_comp_funct_name, - }, - .vendor_id = PEN_TCG, - .type = TCG_PTS_REQ_FUNCT_COMP_EVID, - .flags = flags, - .depth = depth, - .comp_vendor_id = vendor_id, - .family = PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM, - .qualifier = qualifier, - .name = name, - ); - - return &this->public.pa_tnc_attribute; -} - - -/** - * Described in header. - */ -pa_tnc_attr_t *tcg_pts_attr_req_funct_comp_evid_create_from_data(chunk_t data) -{ - private_tcg_pts_attr_req_funct_comp_evid_t *this; - - INIT(this, - .public = { - .pa_tnc_attribute = { - .get_vendor_id = _get_vendor_id, - .get_type = _get_type, - .get_value = _get_value, - .get_noskip_flag = _get_noskip_flag, - .set_noskip_flag = _set_noskip_flag, - .build = _build, - .process = _process, - .destroy = _destroy, - }, - .get_flags= _get_flags, - .set_flags= _set_flags, - .get_sub_component_depth = _get_sub_component_depth, - .get_comp_funct_name_vendor_id = _get_comp_funct_name_vendor_id, - .get_family = _get_family, - .get_qualifier = _get_qualifier, - .set_qualifier = _set_qualifier, - .get_comp_funct_name = _get_comp_funct_name, - .set_comp_funct_name = _set_comp_funct_name, - }, - .vendor_id = PEN_TCG, - .type = TCG_PTS_REQ_FUNCT_COMP_EVID, - .value = chunk_clone(data), - ); - - return &this->public.pa_tnc_attribute; -} diff --git a/src/libpts/tcg/tcg_pts_attr_req_funct_comp_evid.h b/src/libpts/tcg/tcg_pts_attr_req_funct_comp_evid.h deleted file mode 100644 index 215ce6408..000000000 --- a/src/libpts/tcg/tcg_pts_attr_req_funct_comp_evid.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2011 Sansar Choinyambuu - * 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 tcg_pts_attr_req_funct_comp_evid tcg_pts_attr_req_funct_comp_evid - * @{ @ingroup tcg_pts_attr_req_funct_comp_evid - */ - -#ifndef TCG_PTS_ATTR_REQ_FUNCT_COMP_EVID_H_ -#define TCG_PTS_ATTR_REQ_FUNCT_COMP_EVID_H_ - -typedef struct tcg_pts_attr_req_funct_comp_evid_t tcg_pts_attr_req_funct_comp_evid_t; -typedef enum pts_attr_req_funct_comp_evid_flag_t pts_attr_req_funct_comp_evid_flag_t; - -#include "tcg_attr.h" -#include "pts/pts_funct_comp_name.h" -#include "pa_tnc/pa_tnc_attr.h" - -/** - * PTS Request Functional Component Evidence Flags - */ -enum pts_attr_req_funct_comp_evid_flag_t { - /** Transitive Trust Chain flag */ - PTS_REQ_FUNC_COMP_FLAG_TTC = (1<<7), - /** Verify Component flag */ - PTS_REQ_FUNC_COMP_FLAG_VER = (1<<6), - /** Current Evidence flag */ - PTS_REQ_FUNC_COMP_FLAG_CURR = (1<<5), - /** PCR Information flag */ - PTS_REQ_FUNC_COMP_FLAG_PCR = (1<<4), -}; - -/** - * Class implementing the TCG PTS Request Functional Component Evidence attribute - * - */ -struct tcg_pts_attr_req_funct_comp_evid_t { - - /** - * Public PA-TNC attribute interface - */ - pa_tnc_attr_t pa_tnc_attribute; - - /** - * Get flags for PTS Request Functional Component Evidence - * - * @return Set of flags - */ - pts_attr_req_funct_comp_evid_flag_t (*get_flags)(tcg_pts_attr_req_funct_comp_evid_t *this); - - /** - * Set flags for PTS Request Functional Component Evidence - * - * @param flags Set of flags - */ - void (*set_flags)(tcg_pts_attr_req_funct_comp_evid_t *this, - pts_attr_req_funct_comp_evid_flag_t flags); - - /** - * Get Sub-component Depth - * - * @return Sub-component Depth - */ - u_int32_t (*get_sub_component_depth)(tcg_pts_attr_req_funct_comp_evid_t *this); - - /** - * Get Component Functional Name Vendor ID - * - * @return Component Functional Name Vendor ID - */ - u_int32_t (*get_comp_funct_name_vendor_id)(tcg_pts_attr_req_funct_comp_evid_t *this); - - /** - * Get Family - * - * @return Functional Name Family - */ - u_int8_t (*get_family)(tcg_pts_attr_req_funct_comp_evid_t *this); - - /** - * Get Qualifier - * - * @return Functional Name Category Qualifier - */ - pts_qualifier_t (*get_qualifier)(tcg_pts_attr_req_funct_comp_evid_t *this); - - /** - * Set qualifier for Component Functional Name - * - * @param qualifier Functional Name Category Qualifier - */ - void (*set_qualifier)(tcg_pts_attr_req_funct_comp_evid_t *this, - pts_qualifier_t qualifier); - - /** - * Get Component Functional Name - * - * @return Component Functional Name - */ - pts_funct_comp_name_t (*get_comp_funct_name)(tcg_pts_attr_req_funct_comp_evid_t *this); - - - /** - * Set Component Functional Name - * - * @param name Component Functional Name - */ - void (*set_comp_funct_name)(tcg_pts_attr_req_funct_comp_evid_t *this, - pts_funct_comp_name_t name); - - -}; - -/** - * Creates an tcg_pts_attr_req_funct_comp_evid_t object - * - * @param flags Set of flags - * @param depth Sub-component Depth - * @param vendor_id Component Functional Name Vendor ID - * @param qualifier Functional Name Category Qualifier - * @param name Component Functional Name - */ -pa_tnc_attr_t* tcg_pts_attr_req_funct_comp_evid_create(pts_attr_req_funct_comp_evid_flag_t flags, - u_int32_t depth, u_int32_t vendor_id, - pts_qualifier_t qualifier, - pts_funct_comp_name_t name); - -/** - * Creates an tcg_pts_attr_req_funct_comp_evid_t object from received data - * - * @param value Unparsed attribute value - */ -pa_tnc_attr_t* tcg_pts_attr_req_funct_comp_evid_create_from_data(chunk_t value); - -#endif /** TCG_PTS_ATTR_REQ_FUNCT_COMP_EVID_H_ @}*/ diff --git a/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.c b/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.c index 84b31724e..d2c197ac4 100644 --- a/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.c +++ b/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.c @@ -20,6 +20,8 @@ #include <bio/bio_reader.h> #include <debug.h> +#include <time.h> + typedef struct private_tcg_pts_attr_simple_comp_evid_t private_tcg_pts_attr_simple_comp_evid_t; /** @@ -29,37 +31,37 @@ typedef struct private_tcg_pts_attr_simple_comp_evid_t private_tcg_pts_attr_simp * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Flags | Sub-Component Depth | + * | Flags | Sub-Component Depth | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Specific Functional Component | + * | Specific Functional Component | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Specific Functional Component | + * | Specific Functional Component | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Measure. Type | Extended into PCR | + * | Measure. Type | Extended into PCR | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Hash Algorithm | PCR Transform | Reserved | + * | Hash Algorithm | PCR Transform | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Measurement Date/Time | + * | Measurement Date/Time | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Measurement Date/Time | + * | Measurement Date/Time | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Measurement Date/Time | + * | Measurement Date/Time | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Measurement Date/Time | + * | Measurement Date/Time | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Measurement Date/Time | + * | Measurement Date/Time | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Optional Policy URI Length | Opt. Verification Policy URI ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Optional Verification Policy URI ~ + * ~ Optional Verification Policy URI ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Optional PCR Length | Optional PCR Before Value ~ + * | Optional PCR Length | Optional PCR Before Value ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Optional PCR Before Value (Variable Length) ~ + * ~ Optional PCR Before Value (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Optional PCR After Value (Variable Length) ~ + * ~ Optional PCR After Value (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Component Measurement (Variable Length) ~ + * ~ Component Measurement (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ @@ -70,30 +72,22 @@ typedef struct private_tcg_pts_attr_simple_comp_evid_t private_tcg_pts_attr_simp * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Component Functional Name Vendor ID |Fam| Qualifier | + * | Component Functional Name Vendor ID |Fam| Qualifier | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Component Functional Name | + * | Component Functional Name | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ -/** - * Qualifier for Functional Component - * see section 5.2 of PTS Protocol: Binding to TNC IF-M Specification - * - * - * 0 1 2 3 4 5 - * +-+-+-+-+-+-+ - * |K|S| Type | - * +-+-+-+-+-+-+ - */ - - - #define PTS_SIMPLE_COMP_EVID_SIZE 40 -#define PTS_SIMPLE_COMP_EVID_MEASUREMENT_TIME_SIZE 20 +#define PTS_SIMPLE_COMP_EVID_MEAS_TIME_SIZE 20 #define PTS_SIMPLE_COMP_EVID_RESERVED 0x00 -#define PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM 0x00 +#define PTS_SIMPLE_COMP_EVID_FAMILY_MASK 0xC0 +#define PTS_SIMPLE_COMP_EVID_VALIDATION_MASK 0x60 +#define PTS_SIMPLE_COMP_EVID_MEAS_TYPE (1<<7) +#define PTS_SIMPLE_COMP_EVID_FLAG_PCR (1<<7) + +static char *utc_undefined_time_str = "0000-00-00T00:00:00Z"; /** * Private data of an tcg_pts_attr_simple_comp_evid_t object. @@ -126,80 +120,14 @@ struct private_tcg_pts_attr_simple_comp_evid_t { bool noskip_flag; /** - * Set of flags for Simple Component Evidence + * PTS Component Evidence */ - pts_attr_simple_comp_evid_flag_t flags; + pts_comp_evidence_t *evidence; /** - * Sub-component Depth - */ - u_int32_t depth; - - /** - * Component Functional Name Vendor ID - */ - u_int32_t comp_vendor_id; - - /** - * Functional Name Encoding Family - */ - u_int8_t family; - - /** - * Functional Name Category Qualifier - */ - pts_qualifier_t qualifier; - - /** - * Component Functional Name - */ - pts_funct_comp_name_t name; - - /** - * Measurement type - */ - u_int8_t measurement_type; - - /** - * Which PCR the functional component is extended into - */ - u_int32_t extended_pcr; - - /** - * Hash Algorithm + * Reference count */ - pts_meas_algorithms_t hash_algorithm; - - /** - * Transformation type for PCR - */ - pts_pcr_transform_t transformation; - - /** - * Measurement time - */ - chunk_t measurement_time; - - /** - * Optional Policy URI - */ - chunk_t policy_uri; - - /** - * Optional PCR before value - */ - chunk_t pcr_before; - - /** - * Optional PCR after value - */ - chunk_t pcr_after; - - /** - * Component Measurement - */ - chunk_t measurement; - + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -232,101 +160,157 @@ METHOD(pa_tnc_attr_t, set_noskip_flag,void, this->noskip_flag = noskip; } -METHOD(pa_tnc_attr_t, build, void, - private_tcg_pts_attr_simple_comp_evid_t *this) +/** + * Convert time_t to Simple Component Evidence UTS string format + */ +void measurement_time_to_utc(time_t measurement_time, chunk_t *utc_time) { - bio_writer_t *writer; - u_int8_t flags = 0; - u_int8_t qualifier = 0; - - writer = bio_writer_create(PTS_SIMPLE_COMP_EVID_SIZE); - - /* Determine the flags to set*/ - if (this->flags & PTS_SIMPLE_COMP_EVID_FLAG_PCR) - { - flags += 128; - } - if (this->flags & PTS_SIMPLE_COMP_EVID_FLAG_NO_VER) - { - flags += 32; - } - else if (this->flags & PTS_SIMPLE_COMP_EVID_FLAG_VER_FAIL) + struct tm t; + + if (measurement_time == UNDEFINED_TIME) { - flags += 64; + utc_time->ptr = utc_undefined_time_str; } - else if (this->flags & PTS_SIMPLE_COMP_EVID_FLAG_VER_PASS) + else { - flags += 96; + gmtime_r(&measurement_time, &t); + sprintf(utc_time->ptr, "%04d-%02d-%02dT%02d:%02d:%02dZ", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec); } +} + +METHOD(pa_tnc_attr_t, build, void, + private_tcg_pts_attr_simple_comp_evid_t *this) +{ + bio_writer_t *writer; + bool has_pcr_info; + char utc_time_buf[25]; + u_int8_t flags; + u_int32_t depth, extended_pcr; + pts_comp_func_name_t *name; + pts_meas_algorithms_t hash_algorithm; + pts_pcr_transform_t transform; + pts_comp_evid_validation_t validation; + time_t measurement_time; + chunk_t measurement, utc_time, pcr_before, pcr_after, policy_uri; + + /* Extract parameters from comp_evidence_t object */ + name = this->evidence->get_comp_func_name(this->evidence, + &depth); + measurement = this->evidence->get_measurement(this->evidence, + &extended_pcr, &hash_algorithm, &transform, + &measurement_time); + has_pcr_info = this->evidence->get_pcr_info(this->evidence, + &pcr_before, &pcr_after); + validation = this->evidence->get_validation(this->evidence, + &policy_uri); - writer->write_uint8(writer, flags); - - writer->write_uint24 (writer, this->depth); - writer->write_uint24 (writer, this->comp_vendor_id); - - if (this->family != PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM) + /* Determine the flags to set*/ + flags = validation; + if (has_pcr_info) { - DBG1(DBG_TNC, "Functional Name Encoding Family is not set to 00"); + flags |= PTS_SIMPLE_COMP_EVID_FLAG_PCR; } + + utc_time = chunk_create(utc_time_buf, PTS_SIMPLE_COMP_EVID_MEAS_TIME_SIZE); + measurement_time_to_utc(measurement_time, &utc_time); + + writer = bio_writer_create(PTS_SIMPLE_COMP_EVID_SIZE); + + writer->write_uint8 (writer, flags); + writer->write_uint24(writer, depth); + writer->write_uint24(writer, name->get_vendor_id(name)); + writer->write_uint8 (writer, name->get_qualifier(name)); + writer->write_uint32(writer, name->get_name(name)); + writer->write_uint8 (writer, PTS_SIMPLE_COMP_EVID_MEAS_TYPE); + writer->write_uint24(writer, extended_pcr); + writer->write_uint16(writer, hash_algorithm); + writer->write_uint8 (writer, transform); + writer->write_uint8 (writer, PTS_SIMPLE_COMP_EVID_RESERVED); + writer->write_data (writer, utc_time); - qualifier += this->qualifier.type; - if (this->qualifier.kernel) + /* Optional fields */ + if (validation == PTS_COMP_EVID_VALIDATION_FAILED || + validation == PTS_COMP_EVID_VALIDATION_PASSED) { - qualifier += 16; + writer->write_uint16(writer, policy_uri.len); + writer->write_data (writer, policy_uri); } - if (this->qualifier.sub_component) + if (has_pcr_info) { - qualifier += 32; + writer->write_uint16(writer, pcr_before.len); + writer->write_data (writer, pcr_before); + writer->write_data (writer, pcr_after); } + + writer->write_data(writer, measurement); - /* Unknown or Wildcard should not be used for Qualification*/ - if (!qualifier || qualifier == 63) + this->value = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); +} + +static const int days[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; +static const int tm_leap_1970 = 477; + +/** + * Convert Simple Component Evidence UTS string format to time_t + */ +bool measurement_time_from_utc(time_t *measurement_time, chunk_t utc_time) +{ + int tm_year, tm_mon, tm_day, tm_days, tm_hour, tm_min, tm_sec, tm_secs; + int tm_leap_4, tm_leap_100, tm_leap_400, tm_leap; + + if (memeq(utc_undefined_time_str, utc_time.ptr, utc_time.len)) { - DBG1(DBG_TNC, "Unknown or Wildcard should not be used for" - " Functional Name Qualifier"); + *measurement_time = 0; + return TRUE; } - - writer->write_uint8 (writer, qualifier); - writer->write_uint32(writer, this->name); - - writer->write_uint8 (writer, (this->measurement_type << 7)); - writer->write_uint24(writer, this->extended_pcr); - writer->write_uint16(writer, this->hash_algorithm); - writer->write_uint8 (writer, this->transformation); - writer->write_data (writer, this->measurement_time); - - /* Optional fields */ - if (this->policy_uri.ptr && this->policy_uri.len > 0) + if (sscanf(utc_time.ptr, "%4d-%2d-%2dT%2d:%2d:%2dZ", + &tm_year, &tm_mon, &tm_day, &tm_hour, &tm_min, &tm_sec) != 6) { - writer->write_uint16(writer, this->policy_uri.len); - writer->write_data (writer, this->policy_uri); + return FALSE; } - if (this->pcr_before.ptr && this->pcr_after.ptr && - this->pcr_before.len == this->pcr_after.len && - this->pcr_before.len > 0 && this->pcr_after.len > 0) + + /* representation of months as 0..11 */ + tm_mon--; + + /* representation of days as 0..30 */ + tm_day--; + + /* number of leap years between last year and 1970? */ + tm_leap_4 = (tm_year - 1) / 4; + tm_leap_100 = tm_leap_4 / 25; + tm_leap_400 = tm_leap_100 / 4; + tm_leap = tm_leap_4 - tm_leap_100 + tm_leap_400 - tm_leap_1970; + + /* if date later then February, is the current year a leap year? */ + if (tm_mon > 1 && (tm_year % 4 == 0) && + (tm_year % 100 != 0 || tm_year % 400 == 0)) { - writer->write_uint16(writer, this->pcr_before.len); - writer->write_data (writer, this->pcr_before); - writer->write_data (writer, this->pcr_after); + tm_leap++; } - - writer->write_data (writer, this->measurement); - - this->value = chunk_clone(writer->get_buf(writer)); - writer->destroy(writer); + tm_days = 365 * (tm_year - 1970) + days[tm_mon] + tm_day + tm_leap; + tm_secs = 60 * (60 * (24 * tm_days + tm_hour) + tm_min) + tm_sec; + + *measurement_time = tm_secs; + return TRUE; } METHOD(pa_tnc_attr_t, process, status_t, private_tcg_pts_attr_simple_comp_evid_t *this, u_int32_t *offset) { bio_reader_t *reader; - u_int8_t flags; - u_int8_t fam_and_qualifier; - u_int8_t measurement_type; - u_int16_t algorithm; - u_int8_t transformation; - u_int32_t measurement_len; - + pts_comp_func_name_t *name; + u_int8_t flags, fam_and_qualifier, qualifier, reserved; + u_int8_t measurement_type, transform, validation; + u_int16_t hash_algorithm, len; + u_int32_t depth, vendor_id, comp_name, extended_pcr; + chunk_t measurement, utc_time, policy_uri, pcr_before, pcr_after; + time_t measurement_time; + bool has_pcr_info = FALSE, has_validation = FALSE; + status_t status = FAILED; + if (this->value.len < PTS_SIMPLE_COMP_EVID_SIZE) { DBG1(DBG_TNC, "insufficient data for Simple Component Evidence"); @@ -335,315 +319,144 @@ METHOD(pa_tnc_attr_t, process, status_t, } reader = bio_reader_create(this->value); - reader->read_uint8(reader, &flags); - - /* Determine the flags to set*/ - if ((flags >> 7) & 1) - { - this->flags |= PTS_SIMPLE_COMP_EVID_FLAG_PCR; - } - if (!((flags >> 6) & 1) && !((flags >> 5) & 1)) - { - this->flags |= PTS_SIMPLE_COMP_EVID_FLAG_NO_VALID; - } - else if (!((flags >> 6) & 1) && ((flags >> 5) & 1)) - { - this->flags |= PTS_SIMPLE_COMP_EVID_FLAG_NO_VER; - } - else if (((flags >> 6) & 1) && !((flags >> 5) & 1)) - { - this->flags |= PTS_SIMPLE_COMP_EVID_FLAG_VER_FAIL; - } - else if (((flags >> 6) & 1) && ((flags >> 5) & 1)) - { - this->flags |= PTS_SIMPLE_COMP_EVID_FLAG_VER_PASS; - } - - reader->read_uint24(reader, &this->depth); - reader->read_uint24(reader, &this->comp_vendor_id); - reader->read_uint8(reader, &fam_and_qualifier); - - if (((fam_and_qualifier >> 6) & 1) ) - { - this->family += 1; - } - if (((fam_and_qualifier >> 7) & 1) ) + reader->read_uint8 (reader, &flags); + reader->read_uint24(reader, &depth); + reader->read_uint24(reader, &vendor_id); + reader->read_uint8 (reader, &fam_and_qualifier); + reader->read_uint32(reader, &comp_name); + reader->read_uint8 (reader, &measurement_type); + reader->read_uint24(reader, &extended_pcr); + reader->read_uint16(reader, &hash_algorithm); + reader->read_uint8 (reader, &transform); + reader->read_uint8 (reader, &reserved); + reader->read_data (reader, PTS_SIMPLE_COMP_EVID_MEAS_TIME_SIZE, &utc_time); + + if (measurement_type != PTS_SIMPLE_COMP_EVID_MEAS_TYPE) { - this->family += 2; - } - - /* TODO: Generate an IF-M error attribute indicating */ - /* TCG_PTS_INVALID_NAME_FAM */ - //if (&this->comp_vendor_id==PEN_TCG && this->family != PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM) - //{ - // DBG1(DBG_TNC, "Functional Name Encoding Family is not set to 00"); - //} - - if (((fam_and_qualifier >> 5) & 1) ) - { - this->qualifier.kernel = true; + DBG1(DBG_TNC, "unsupported Measurement Type in " + "Simple Component Evidence"); + *offset = 12; + reader->destroy(reader); + return FAILED; } - if (((fam_and_qualifier >> 4) & 1) ) + if (!measurement_time_from_utc(&measurement_time, utc_time)) { - this->qualifier.sub_component = true; + DBG1(DBG_TNC, "invalid Measurement Time field in " + "Simple Component Evidence"); + *offset = 20; + reader->destroy(reader); + return FAILED; } - this->qualifier.type = ( fam_and_qualifier & 0xF ); - /* TODO: Check the type is defined in pts_attr_req_funct_comp_type_t */ + validation = flags & PTS_SIMPLE_COMP_EVID_VALIDATION_MASK; + qualifier = fam_and_qualifier & ~PTS_SIMPLE_COMP_EVID_FAMILY_MASK; - /* Unknown or Wildcard should not be used for Qualification*/ - if (!(fam_and_qualifier & 0x3F) || (fam_and_qualifier & 0x3F) == 0x3F) + /* Is optional Policy URI field included? */ + if (validation == PTS_COMP_EVID_VALIDATION_FAILED || + validation == PTS_COMP_EVID_VALIDATION_PASSED) { - DBG1(DBG_TNC, "Unknown or Wildcard should not be used for" - " Functional Name Qualifier"); + if (!reader->read_uint16(reader, &len)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Component Evidence " + "Verification Policy URI Length"); + goto end; + } + if (!reader->read_data(reader, len, &policy_uri)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Component Evidence " + "Verification Policy URI"); + goto end; + } + has_validation = TRUE; } - reader->read_uint32(reader, &this->name); - /* TODO: Check the name is defined in pts_funct_comp_name_t */ - - reader->read_uint8(reader, &measurement_type); - this->measurement_type = (measurement_type >> 7 ) & 1; - - reader->read_uint24(reader, &this->extended_pcr); - reader->read_uint16(reader, &algorithm); - this->hash_algorithm = algorithm; - - reader->read_uint8(reader, &transformation); - this->transformation = transformation; - /* TODO: Check the transformation is defined in pts_pcr_transform_t */ - - reader->read_data(reader, PTS_SIMPLE_COMP_EVID_MEASUREMENT_TIME_SIZE, - &this->measurement_time); - this->measurement_time = chunk_clone(this->measurement_time); - - /* Optional Policy URI field is included */ - if (this->flags & PTS_SIMPLE_COMP_EVID_FLAG_VER_FAIL || - this->flags & PTS_SIMPLE_COMP_EVID_FLAG_VER_PASS) - { - u_int16_t policy_uri_len; - reader->read_uint16(reader, &policy_uri_len); - reader->read_data(reader, policy_uri_len, &this->policy_uri); - this->policy_uri = chunk_clone(this->policy_uri); - } - - /* Optional PCR value fields are included */ - if (this->flags & PTS_SIMPLE_COMP_EVID_FLAG_PCR) + /* Are optional PCR value fields included? */ + if (flags & PTS_SIMPLE_COMP_EVID_FLAG_PCR) { - u_int16_t pcr_value_len; - reader->read_uint16(reader, &pcr_value_len); - reader->read_data(reader, pcr_value_len, &this->pcr_before); - this->pcr_before = chunk_clone(this->pcr_before); - reader->read_data(reader, pcr_value_len, &this->pcr_after); - this->pcr_after = chunk_clone(this->pcr_after); + if (!reader->read_uint16(reader, &len)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Component Evidence " + "PCR Value length"); + goto end; + } + if (!reader->read_data(reader, len, &pcr_before)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Component Evidence " + "PCR Before Value"); + goto end; + } + if (!reader->read_data(reader, len, &pcr_after)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Component Evidence " + "PCR After Value"); + goto end; + } + has_pcr_info = TRUE; } - - measurement_len = reader->remaining(reader); - reader->read_data(reader, measurement_len, &this->measurement); - this->measurement = chunk_clone(this->measurement); + /* Measurement field comes at the very end */ + reader->read_data(reader,reader->remaining(reader), &measurement); reader->destroy(reader); - return SUCCESS; -} - -METHOD(pa_tnc_attr_t, destroy, void, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - free(this->value.ptr); - free(this->measurement_time.ptr); - free(this->policy_uri.ptr); - free(this->pcr_before.ptr); - free(this->pcr_after.ptr); - free(this->measurement.ptr); - free(this); -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_flags, pts_attr_simple_comp_evid_flag_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->flags; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_flags, void, - private_tcg_pts_attr_simple_comp_evid_t *this, pts_attr_simple_comp_evid_flag_t flags) -{ - this->flags = flags; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_sub_component_depth, u_int32_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->depth; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_spec_comp_funct_name_vendor_id, u_int32_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->comp_vendor_id; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_family, u_int8_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->family; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_qualifier, pts_qualifier_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->qualifier; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_qualifier, void, - private_tcg_pts_attr_simple_comp_evid_t *this, - pts_qualifier_t qualifier) -{ - this->qualifier = qualifier; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_comp_funct_name, pts_funct_comp_name_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->name; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_comp_funct_name, void, - private_tcg_pts_attr_simple_comp_evid_t *this, pts_funct_comp_name_t name) -{ - this->name = name; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_measurement_type, u_int8_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->measurement_type; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_extended_pcr, u_int32_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->extended_pcr; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_extended_pcr, void, - private_tcg_pts_attr_simple_comp_evid_t *this, u_int32_t extended_pcr) -{ - this->extended_pcr = extended_pcr; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_hash_algorithm, pts_meas_algorithms_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->hash_algorithm; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_hash_algorithm, void, - private_tcg_pts_attr_simple_comp_evid_t *this, - pts_meas_algorithms_t hash_algorithm) -{ - this->hash_algorithm = hash_algorithm; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_pcr_trans, pts_pcr_transform_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->transformation; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_pcr_trans, void, - private_tcg_pts_attr_simple_comp_evid_t *this, pts_pcr_transform_t transformation) -{ - this->transformation = transformation; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, get_measurement_time, chunk_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->measurement_time; -} -METHOD(tcg_pts_attr_simple_comp_evid_t, set_measurement_time, void, - private_tcg_pts_attr_simple_comp_evid_t *this, chunk_t measurement_time) -{ - this->measurement_time = measurement_time; -} + /* Create Component Functional Name object */ + name = pts_comp_func_name_create(vendor_id, comp_name, qualifier); -METHOD(tcg_pts_attr_simple_comp_evid_t, get_policy_uri, chunk_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->policy_uri; -} + /* Create Component Evidence object */ + measurement = chunk_clone(measurement); + this->evidence = pts_comp_evidence_create(name, depth, extended_pcr, + hash_algorithm, transform, + measurement_time, measurement); -METHOD(tcg_pts_attr_simple_comp_evid_t, set_policy_uri, void, - private_tcg_pts_attr_simple_comp_evid_t *this, chunk_t policy_uri) -{ - this->policy_uri = policy_uri; -} + /* Add options */ + if (has_validation) + { + policy_uri = chunk_clone(policy_uri); + this->evidence->set_validation(this->evidence, validation, policy_uri); + } + if (has_pcr_info) + { + pcr_before = chunk_clone(pcr_before); + pcr_after = chunk_clone(pcr_after); + this->evidence->set_pcr_info(this->evidence, pcr_before, pcr_after); + } -METHOD(tcg_pts_attr_simple_comp_evid_t, get_pcr_before_value, chunk_t, - private_tcg_pts_attr_simple_comp_evid_t *this) -{ - return this->pcr_before; -} + return SUCCESS; -METHOD(tcg_pts_attr_simple_comp_evid_t, set_pcr_before_value, void, - private_tcg_pts_attr_simple_comp_evid_t *this, chunk_t pcr_before) -{ - this->pcr_before = pcr_before; +end: + reader->destroy(reader); + return status; } -METHOD(tcg_pts_attr_simple_comp_evid_t, get_pcr_after_value, chunk_t, +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, private_tcg_pts_attr_simple_comp_evid_t *this) { - return this->pcr_after; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_pcr_after_value, void, - private_tcg_pts_attr_simple_comp_evid_t *this, chunk_t pcr_after) -{ - this->pcr_after = pcr_after; + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; } -METHOD(tcg_pts_attr_simple_comp_evid_t, get_pcr_len, u_int16_t, +METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_simple_comp_evid_t *this) { - if (this->pcr_before.ptr && this->pcr_after.ptr && - this->pcr_before.len == this->pcr_after.len && - this->pcr_before.len > 0 && this->pcr_after.len > 0) + if (ref_put(&this->ref)) { - return this->pcr_before.len; + this->evidence->destroy(this->evidence); + free(this->value.ptr); + free(this); } - return 0; } -METHOD(tcg_pts_attr_simple_comp_evid_t, get_comp_measurement, chunk_t, +METHOD(tcg_pts_attr_simple_comp_evid_t, get_comp_evidence, pts_comp_evidence_t*, private_tcg_pts_attr_simple_comp_evid_t *this) { - return this->measurement; -} - -METHOD(tcg_pts_attr_simple_comp_evid_t, set_comp_measurement, void, - private_tcg_pts_attr_simple_comp_evid_t *this, chunk_t measurement) -{ - this->measurement = measurement; + return this->evidence; } /** * Described in header. */ -pa_tnc_attr_t *tcg_pts_attr_simple_comp_evid_create( - pts_attr_simple_comp_evid_flag_t flags, - u_int32_t depth, u_int32_t vendor_id, - pts_qualifier_t qualifier, - pts_funct_comp_name_t name, - u_int32_t extended_pcr, - pts_meas_algorithms_t hash_algorithm, - pts_pcr_transform_t transformation, - chunk_t measurement_time, - chunk_t policy_uri, - chunk_t pcr_before, chunk_t pcr_after, - chunk_t measurement) +pa_tnc_attr_t *tcg_pts_attr_simple_comp_evid_create(pts_comp_evidence_t *evid) { private_tcg_pts_attr_simple_comp_evid_t *this; - + INIT(this, .public = { .pa_tnc_attribute = { @@ -654,52 +467,15 @@ pa_tnc_attr_t *tcg_pts_attr_simple_comp_evid_create( .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, - .get_flags= _get_flags, - .set_flags= _set_flags, - .get_sub_component_depth = _get_sub_component_depth, - .get_spec_comp_funct_name_vendor_id = _get_spec_comp_funct_name_vendor_id, - .get_family = _get_family, - .get_qualifier = _get_qualifier, - .set_qualifier = _set_qualifier, - .get_comp_funct_name = _get_comp_funct_name, - .set_comp_funct_name = _set_comp_funct_name, - .get_measurement_type = _get_measurement_type, - .get_extended_pcr = _get_extended_pcr, - .set_extended_pcr = _set_extended_pcr, - .get_hash_algorithm = _get_hash_algorithm, - .set_hash_algorithm = _set_hash_algorithm, - .get_pcr_trans = _get_pcr_trans, - .set_pcr_trans = _set_pcr_trans, - .get_measurement_time = _get_measurement_time, - .set_measurement_time = _set_measurement_time, - .get_policy_uri = _get_policy_uri, - .set_policy_uri = _set_policy_uri, - .get_pcr_before_value = _get_pcr_before_value, - .set_pcr_before_value = _set_pcr_before_value, - .get_pcr_after_value = _get_pcr_after_value, - .set_pcr_after_value = _set_pcr_after_value, - .get_pcr_len = _get_pcr_len, - .get_comp_measurement = _get_comp_measurement, - .set_comp_measurement = _set_comp_measurement, + .get_comp_evidence = _get_comp_evidence, }, .vendor_id = PEN_TCG, .type = TCG_PTS_SIMPLE_COMP_EVID, - .flags = flags, - .depth = depth, - .comp_vendor_id = vendor_id, - .family = PTS_REQ_FUNCT_COMP_FAM_BIN_ENUM, - .qualifier = qualifier, - .name = name, - .extended_pcr = extended_pcr, - .hash_algorithm = hash_algorithm, - .transformation = transformation, - .measurement_time = measurement_time, - .policy_uri = policy_uri, - .pcr_before = pcr_before, - .pcr_after = pcr_after, - .measurement = measurement, + .evidence = evid, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -723,39 +499,15 @@ pa_tnc_attr_t *tcg_pts_attr_simple_comp_evid_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, - .get_flags= _get_flags, - .set_flags= _set_flags, - .get_sub_component_depth = _get_sub_component_depth, - .get_spec_comp_funct_name_vendor_id = _get_spec_comp_funct_name_vendor_id, - .get_family = _get_family, - .get_qualifier = _get_qualifier, - .set_qualifier = _set_qualifier, - .get_comp_funct_name = _get_comp_funct_name, - .set_comp_funct_name = _set_comp_funct_name, - .get_measurement_type = _get_measurement_type, - .get_extended_pcr = _get_extended_pcr, - .set_extended_pcr = _set_extended_pcr, - .get_hash_algorithm = _get_hash_algorithm, - .set_hash_algorithm = _set_hash_algorithm, - .get_pcr_trans = _get_pcr_trans, - .set_pcr_trans = _set_pcr_trans, - .get_measurement_time = _get_measurement_time, - .set_measurement_time = _set_measurement_time, - .get_policy_uri = _get_policy_uri, - .set_policy_uri = _set_policy_uri, - .get_pcr_before_value = _get_pcr_before_value, - .set_pcr_before_value = _set_pcr_before_value, - .get_pcr_after_value = _get_pcr_after_value, - .set_pcr_after_value = _set_pcr_after_value, - .get_pcr_len = _get_pcr_len, - .get_comp_measurement = _get_comp_measurement, - .set_comp_measurement = _set_comp_measurement, + .get_comp_evidence = _get_comp_evidence, }, .vendor_id = PEN_TCG, .type = TCG_PTS_SIMPLE_COMP_EVID, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.h b/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.h index 5da20e96e..3a80904c8 100644 --- a/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.h +++ b/src/libpts/tcg/tcg_pts_attr_simple_comp_evid.h @@ -22,45 +22,12 @@ #define TCG_PTS_ATTR_SIMPLE_COMP_EVID_H_ typedef struct tcg_pts_attr_simple_comp_evid_t tcg_pts_attr_simple_comp_evid_t; -typedef enum pts_attr_simple_comp_evid_flag_t pts_attr_simple_comp_evid_flag_t; -typedef enum pts_pcr_transform_t pts_pcr_transform_t; #include "tcg_attr.h" -#include "pts/pts_meas_algo.h" -#include "pts/pts_funct_comp_name.h" +#include "pts/components/pts_comp_evidence.h" #include "pa_tnc/pa_tnc_attr.h" /** - * PTS Simple Component Evidence Flags - */ -enum pts_attr_simple_comp_evid_flag_t { - /** PCR information fields inlcuded */ - PTS_SIMPLE_COMP_EVID_FLAG_PCR = 0, - /** No Validation was attempted */ - PTS_SIMPLE_COMP_EVID_FLAG_NO_VALID = 1, - /** Attempted validation, unable to verify */ - PTS_SIMPLE_COMP_EVID_FLAG_NO_VER = 2, - /** Attempted validation, verification failed */ - PTS_SIMPLE_COMP_EVID_FLAG_VER_FAIL = 3, - /** Attempted validation, verification passed */ - PTS_SIMPLE_COMP_EVID_FLAG_VER_PASS = 4, -}; - -/** - * PTS PCR Transformations - */ -enum pts_pcr_transform_t { - /** No Transformation */ - PTS_PCR_TRANSFORM_NO = 0, - /** Hash Value matched PCR size */ - PTS_PCR_TRANSFORM_MATCH = 1, - /** Hash value shorter than PCR size */ - PTS_PCR_TRANSFORM_SHORT = 2, - /** Hash value longer than PCR size */ - PTS_PCR_TRANSFORM_LONG = 3, -}; - -/** * Class implementing the TCG PTS Simple Component Evidence attribute * */ @@ -70,240 +37,22 @@ struct tcg_pts_attr_simple_comp_evid_t { * Public PA-TNC attribute interface */ pa_tnc_attr_t pa_tnc_attribute; - - /** - * Get flags for PTS Simple Component Evidence - * - * @return Set of flags - */ - pts_attr_simple_comp_evid_flag_t (*get_flags)(tcg_pts_attr_simple_comp_evid_t *this); /** - * Set flags for PTS Simple Component Evidence - * - * @param flags Set of flags - */ - void (*set_flags)(tcg_pts_attr_simple_comp_evid_t *this, - pts_attr_simple_comp_evid_flag_t flags); - - /** - * Get Sub-component Depth - * - * @return Sub-component Depth - */ - u_int32_t (*get_sub_component_depth)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Get Specific Component Functional Name Vendor ID - * - * @return Component Functional Name Vendor ID - */ - u_int32_t (*get_spec_comp_funct_name_vendor_id)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Get Family - * - * @return Functional Name Family - */ - u_int8_t (*get_family)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Get Qualifier - * - * @return Functional Name Category Qualifier - */ - pts_qualifier_t (*get_qualifier)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set qualifier for Component Functional Name - * - * @param qualifier Functional Name Category Qualifier - */ - void (*set_qualifier)(tcg_pts_attr_simple_comp_evid_t *this, - pts_qualifier_t qualifier); - - /** - * Get Special Component Functional Name - * - * @return Component Functional Name - */ - pts_funct_comp_name_t (*get_comp_funct_name)(tcg_pts_attr_simple_comp_evid_t *this); - - - /** - * Set Component Functional Name - * - * @param name Component Functional Name - */ - void (*set_comp_funct_name)(tcg_pts_attr_simple_comp_evid_t *this, - pts_funct_comp_name_t name); - - /** - * Get Measurement Type - * - * @return Measurement Type - */ - u_int8_t (*get_measurement_type)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Get which PCR the functional component is extended into - * - * @return Number of PCR - */ - u_int32_t (*get_extended_pcr)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set which PCR the functional component is extended into - * - * @param pcr_number Number of PCR - */ - void (*set_extended_pcr)(tcg_pts_attr_simple_comp_evid_t *this, - u_int32_t extended_pcr); - - /** - * Get Hash Algorithm - * - * @return Hash Algorithm - */ - pts_meas_algorithms_t (*get_hash_algorithm)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set Hash Algorithm - * - * @param hash_algorithm Hash Algorithm - */ - void (*set_hash_algorithm)(tcg_pts_attr_simple_comp_evid_t *this, - pts_meas_algorithms_t hash_algorithm); - - /** - * Get PCR Transformation - * - * @return Transformation type of PCR - */ - pts_pcr_transform_t (*get_pcr_trans)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set PCR Transformation - * - * @param transformation Transformation type of PCR - */ - void (*set_pcr_trans)(tcg_pts_attr_simple_comp_evid_t *this, - pts_pcr_transform_t transformation); - - /** - * Get Measurement Time - * - * @return Measurement time - */ - chunk_t (*get_measurement_time)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set Measurement Time - * - * @param time Measurement time - */ - void (*set_measurement_time)(tcg_pts_attr_simple_comp_evid_t *this, - chunk_t time); - - /** - * Get Optional Policy URI - * - * @return Policy URI - */ - chunk_t (*get_policy_uri)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set Optional Policy URI - * - * @param policy_uri Policy URI - */ - void (*set_policy_uri)(tcg_pts_attr_simple_comp_evid_t *this, - chunk_t policy_uri); - - /** - * Get Optional PCR Length - * - * @return Length of PCR before/after values - */ - u_int16_t (*get_pcr_len)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Get Optional PCR before value - * - * @return PCR before value - */ - chunk_t (*get_pcr_before_value)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set Optional PCR before value - * - * @param pcr_before PCR before value - */ - void (*set_pcr_before_value)(tcg_pts_attr_simple_comp_evid_t *this, - chunk_t pcr_before); - - /** - * Get Optional PCR after value - * - * @return PCR after value - */ - chunk_t (*get_pcr_after_value)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set Optional PCR after value - * - * @param pcr_after PCR after value - */ - void (*set_pcr_after_value)(tcg_pts_attr_simple_comp_evid_t *this, - chunk_t pcr_after); - - /** - * Get Component Measurement - * - * @return Component Measurement Hash - */ - chunk_t (*get_comp_measurement)(tcg_pts_attr_simple_comp_evid_t *this); - - /** - * Set Component Measurement + * Get Component Evidence * - * @param measurement Component Measurement Hash + * @return Component Evidence */ - void (*set_comp_measurement)(tcg_pts_attr_simple_comp_evid_t *this, - chunk_t measurement); + pts_comp_evidence_t* (*get_comp_evidence)(tcg_pts_attr_simple_comp_evid_t *this); }; /** * Creates an tcg_pts_attr_simple_comp_evid_t object * - * @param flags Set of flags - * @param depth Sub-component Depth - * @param vendor_id Component Functional Name Vendor ID - * @param qualifier Functional Name Category Qualifier - * @param name Component Functional Name - * @param extended_pcr Which PCR the functional component is extended into - * @param hash_algorithm Hash Algorithm - * @param transformation Transformation type for PCR - * @param measurement_time Measurement time - * @param policy_uri Optional Policy URI - * @param pcr_before Optional PCR before value - * @param pcr_after Optional PCR after value - * @param measurement Component Measurement + * @param evid Component Evidence */ -pa_tnc_attr_t* tcg_pts_attr_simple_comp_evid_create(pts_attr_simple_comp_evid_flag_t flags, - u_int32_t depth, - u_int32_t vendor_id, - pts_qualifier_t qualifier, - pts_funct_comp_name_t name, - u_int32_t extended_pcr, - pts_meas_algorithms_t hash_algorithm, - pts_pcr_transform_t transformation, - chunk_t measurement_time, - chunk_t policy_uri, - chunk_t pcr_before, - chunk_t pcr_after, - chunk_t measurement); +pa_tnc_attr_t* tcg_pts_attr_simple_comp_evid_create(pts_comp_evidence_t *evid); /** * Creates an tcg_pts_attr_simple_comp_evid_t object from received data diff --git a/src/libpts/tcg/tcg_pts_attr_simple_evid_final.c b/src/libpts/tcg/tcg_pts_attr_simple_evid_final.c index fa2f6e5c6..27720d509 100644 --- a/src/libpts/tcg/tcg_pts_attr_simple_evid_final.c +++ b/src/libpts/tcg/tcg_pts_attr_simple_evid_final.c @@ -14,6 +14,7 @@ */ #include "tcg_pts_attr_simple_evid_final.h" +#include "pts/pts_simple_evid_final.h" #include <pa_tnc/pa_tnc_msg.h> #include <bio/bio_writer.h> @@ -29,23 +30,23 @@ typedef struct private_tcg_pts_attr_simple_evid_final_t private_tcg_pts_attr_sim * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Flags | Reserved | Optional Composite Hash Alg | + * | Flags | Reserved | Optional Composite Hash Alg | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Optional TPM PCR Composite Length | + * | Optional TPM PCR Composite Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Optional TPM PCR Composite (Variable Length) ~ + * ~ Optional TPM PCR Composite (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Optional TPM Quote Signature Length | + * | Optional TPM Quote Signature Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Optional TPM Quote Signature (Variable Length) ~ + * ~ Optional TPM Quote Signature (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * ~ Optional Evidence Signature (Variable Length) ~ + * ~ Optional Evidence Signature (Variable Length) ~ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -#define PTS_SIMPLE_EVID_FINAL_SIZE 4 +#define PTS_SIMPLE_EVID_FINAL_SIZE 2 #define PTS_SIMPLE_EVID_FINAL_RESERVED 0x00 - +#define PTS_SIMPLE_EVID_FINAL_FLAG_MASK 0xC0 /** * Private data of an tcg_pts_attr_simple_evid_final_t object. */ @@ -75,11 +76,11 @@ struct private_tcg_pts_attr_simple_evid_final_t { * Noskip flag */ bool noskip_flag; - + /** * Set of flags for Simple Evidence Final */ - pts_simple_evid_final_flag_t flags; + u_int8_t flags; /** * Optional Composite Hash Algorithm @@ -94,13 +95,22 @@ struct private_tcg_pts_attr_simple_evid_final_t { /** * Optional TPM Quote Signature */ - chunk_t tpm_quote_sign; + chunk_t tpm_quote_sig; + + /** + * Is Evidence Signature included? + */ + bool has_evid_sig; /** * Optional Evidence Signature */ - chunk_t evid_sign; + chunk_t evid_sig; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -133,49 +143,62 @@ METHOD(pa_tnc_attr_t, set_noskip_flag,void, this->noskip_flag = noskip; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_simple_evid_final_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + +METHOD(pa_tnc_attr_t, destroy, void, + private_tcg_pts_attr_simple_evid_final_t *this) +{ + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this->pcr_comp.ptr); + free(this->tpm_quote_sig.ptr); + free(this->evid_sig.ptr); + free(this); + } +} + METHOD(pa_tnc_attr_t, build, void, private_tcg_pts_attr_simple_evid_final_t *this) { bio_writer_t *writer; - u_int8_t flags = 0; - - writer = bio_writer_create(PTS_SIMPLE_EVID_FINAL_SIZE); + u_int8_t flags; - /* Determine the flags to set*/ - if (this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO) - { - flags += 64; - } - else if (this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2) - { - flags += 128; - } - else if (this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2_CAP_VER) - { - flags += 192; - } - if (this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_EVID) + flags = this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_MASK; + + if (this->has_evid_sig) { - flags += 32; + flags |= PTS_SIMPLE_EVID_FINAL_EVID_SIG; } + + writer = bio_writer_create(PTS_SIMPLE_EVID_FINAL_SIZE); writer->write_uint8 (writer, flags); writer->write_uint8 (writer, PTS_SIMPLE_EVID_FINAL_RESERVED); + + /** Optional Composite Hash Algorithm field is always present + * Field has value of all zeroes if not used. + * Implemented adhering the suggestion of Paul Sangster 28.Oct.2011 + */ writer->write_uint16(writer, this->comp_hash_algorithm); /* Optional fields */ - if (this->pcr_comp.ptr && this->pcr_comp.len > 0) + if (this->flags != PTS_SIMPLE_EVID_FINAL_NO) { writer->write_uint32 (writer, this->pcr_comp.len); writer->write_data (writer, this->pcr_comp); + + writer->write_uint32 (writer, this->tpm_quote_sig.len); + writer->write_data (writer, this->tpm_quote_sig); } - if (this->tpm_quote_sign.ptr && this->tpm_quote_sign.len > 0) - { - writer->write_uint32 (writer, this->tpm_quote_sign.len); - writer->write_data (writer, this->tpm_quote_sign); - } - if (this->evid_sign.ptr && this->evid_sign.len > 0) + + if (this->has_evid_sig) { - writer->write_data (writer, this->evid_sign); + writer->write_data (writer, this->evid_sig); } this->value = chunk_clone(writer->get_buf(writer)); @@ -186,9 +209,10 @@ METHOD(pa_tnc_attr_t, process, status_t, private_tcg_pts_attr_simple_evid_final_t *this, u_int32_t *offset) { bio_reader_t *reader; - u_int8_t flags; - u_int8_t reserved; + u_int8_t flags, reserved; u_int16_t algorithm; + u_int32_t pcr_comp_len, tpm_quote_sig_len, evid_sig_len; + status_t status = FAILED; if (this->value.len < PTS_SIMPLE_EVID_FINAL_SIZE) { @@ -199,157 +223,110 @@ METHOD(pa_tnc_attr_t, process, status_t, reader = bio_reader_create(this->value); reader->read_uint8(reader, &flags); - - /* Determine the flags to set*/ - if (!((flags >> 7) & 1) && !((flags >> 6) & 1)) - { - this->flags |= PTS_SIMPLE_EVID_FINAL_FLAG_NO; - } - else if (!((flags >> 7) & 1) && ((flags >> 6) & 1)) - { - this->flags |= PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO; - } - else if (((flags >> 7) & 1) && !((flags >> 6) & 1)) - { - this->flags |= PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2; - } - else if (((flags >> 7) & 1) && ((flags >> 6) & 1)) - { - this->flags |= PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2_CAP_VER; - } - if ((flags >> 5) & 1) - { - this->flags |= PTS_SIMPLE_EVID_FINAL_FLAG_EVID; - } - reader->read_uint8(reader, &reserved); + + this->flags = flags & PTS_SIMPLE_EVID_FINAL_FLAG_MASK; + + this->has_evid_sig = (flags & PTS_SIMPLE_EVID_FINAL_EVID_SIG) != 0; + + /** Optional Composite Hash Algorithm field is always present + * Field has value of all zeroes if not used. + * Implemented adhering the suggestion of Paul Sangster 28.Oct.2011 + */ + reader->read_uint16(reader, &algorithm); this->comp_hash_algorithm = algorithm; - /* Optional TPM PCR Composite field is included */ - if (!(this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_NO)) + /* Optional Composite Hash Algorithm and TPM PCR Composite fields */ + if (this->flags != PTS_SIMPLE_EVID_FINAL_NO) { - u_int32_t pcr_comp_len; - u_int32_t tpm_quote_sign_len; - reader->read_uint32(reader, &pcr_comp_len); - reader->read_data(reader, pcr_comp_len, &this->pcr_comp); + if (!reader->read_uint32(reader, &pcr_comp_len)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " + "PCR Composite Length"); + goto end; + } + if (!reader->read_data(reader, pcr_comp_len, &this->pcr_comp)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " + "PCR Composite"); + goto end; + } this->pcr_comp = chunk_clone(this->pcr_comp); - reader->read_uint32(reader, &tpm_quote_sign_len); - reader->read_data(reader, tpm_quote_sign_len, &this->tpm_quote_sign); - this->tpm_quote_sign = chunk_clone(this->tpm_quote_sign); + + if (!reader->read_uint32(reader, &tpm_quote_sig_len)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " + "TPM Quote Singature Length"); + goto end; + } + if (!reader->read_data(reader, tpm_quote_sig_len, &this->tpm_quote_sig)) + { + DBG1(DBG_TNC, "insufficient data for PTS Simple Evidence Final " + "TPM Quote Singature"); + goto end; + } + this->tpm_quote_sig = chunk_clone(this->tpm_quote_sig); } - /* Optional Evidence Signature field is included */ - if (this->flags & PTS_SIMPLE_EVID_FINAL_FLAG_EVID) + /* Optional Evidence Signature field */ + if (this->has_evid_sig) { - u_int32_t evid_sign_len = reader->remaining(reader); - reader->read_data(reader, evid_sign_len, &this->evid_sign); - this->evid_sign = chunk_clone(this->evid_sign); + evid_sig_len = reader->remaining(reader); + reader->read_data(reader, evid_sig_len, &this->evid_sig); + this->evid_sig = chunk_clone(this->evid_sig); } reader->destroy(reader); return SUCCESS; -} - -METHOD(pa_tnc_attr_t, destroy, void, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - free(this->value.ptr); - free(this->pcr_comp.ptr); - free(this->tpm_quote_sign.ptr); - free(this->evid_sign.ptr); - free(this); -} - -METHOD(tcg_pts_attr_simple_evid_final_t, get_flags, pts_simple_evid_final_flag_t, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - return this->flags; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, set_flags, void, - private_tcg_pts_attr_simple_evid_final_t *this, pts_simple_evid_final_flag_t flags) -{ - this->flags = flags; -} -METHOD(tcg_pts_attr_simple_evid_final_t, get_comp_hash_algorithm, pts_meas_algorithms_t, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - return this->comp_hash_algorithm; +end: + reader->destroy(reader); + return status; } -METHOD(tcg_pts_attr_simple_evid_final_t, set_comp_hash_algorithm, void, - private_tcg_pts_attr_simple_evid_final_t *this, pts_meas_algorithms_t comp_hash_algorithm) +METHOD(tcg_pts_attr_simple_evid_final_t, get_quote_info, u_int8_t, + private_tcg_pts_attr_simple_evid_final_t *this, + pts_meas_algorithms_t *comp_hash_algo, chunk_t *pcr_comp, chunk_t *tpm_quote_sig) { - this->comp_hash_algorithm = comp_hash_algorithm; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, get_comp_pcr_len, u_int32_t, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - if (this->pcr_comp.ptr && this->pcr_comp.len > 0) + if (comp_hash_algo) { - return this->pcr_comp.len; + *comp_hash_algo = this->comp_hash_algorithm; } - return 0; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, get_pcr_comp, chunk_t, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - return this->pcr_comp; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, set_pcr_comp, void, - private_tcg_pts_attr_simple_evid_final_t *this, chunk_t pcr_comp) -{ - this->pcr_comp = pcr_comp; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, get_tpm_quote_sign_len, u_int32_t, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - if (this->tpm_quote_sign.ptr && this->tpm_quote_sign.len > 0) + if (pcr_comp) { - return this->tpm_quote_sign.len; + *pcr_comp = this->pcr_comp; } - return 0; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, get_tpm_quote_sign, chunk_t, - private_tcg_pts_attr_simple_evid_final_t *this) -{ - return this->tpm_quote_sign; -} - -METHOD(tcg_pts_attr_simple_evid_final_t, set_tpm_quote_sign, void, - private_tcg_pts_attr_simple_evid_final_t *this, chunk_t tpm_quote_sign) -{ - this->tpm_quote_sign = tpm_quote_sign; + if (tpm_quote_sig) + { + *tpm_quote_sig = this->tpm_quote_sig; + } + return this->flags; } -METHOD(tcg_pts_attr_simple_evid_final_t, get_evid_sign, chunk_t, - private_tcg_pts_attr_simple_evid_final_t *this) +METHOD(tcg_pts_attr_simple_evid_final_t, get_evid_sig, bool, + private_tcg_pts_attr_simple_evid_final_t *this, chunk_t *evid_sig) { - return this->evid_sign; + if (evid_sig) + { + *evid_sig = this->evid_sig; + } + return this->has_evid_sig; } -METHOD(tcg_pts_attr_simple_evid_final_t, set_evid_sign, void, - private_tcg_pts_attr_simple_evid_final_t *this, chunk_t evid_sign) +METHOD(tcg_pts_attr_simple_evid_final_t, set_evid_sig, void, + private_tcg_pts_attr_simple_evid_final_t *this, chunk_t evid_sig) { - this->evid_sign = evid_sign; + this->evid_sig = evid_sig; + this->has_evid_sig = TRUE; } /** * Described in header. */ -pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create( - pts_simple_evid_final_flag_t flags, - pts_meas_algorithms_t comp_hash_algorithm, - chunk_t pcr_comp, - chunk_t tpm_quote_sign, - chunk_t evid_sign) +pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create(u_int8_t flags, + pts_meas_algorithms_t comp_hash_algorithm, + chunk_t pcr_comp, chunk_t tpm_quote_sig) { private_tcg_pts_attr_simple_evid_final_t *this; @@ -363,28 +340,20 @@ pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create( .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, - .get_flags= _get_flags, - .set_flags= _set_flags, - .get_comp_hash_algorithm = _get_comp_hash_algorithm, - .set_comp_hash_algorithm = _set_comp_hash_algorithm, - .get_comp_pcr_len = _get_comp_pcr_len, - .get_pcr_comp = _get_pcr_comp, - .set_pcr_comp = _set_pcr_comp, - .get_tpm_quote_sign_len = _get_tpm_quote_sign_len, - .get_tpm_quote_sign = _get_tpm_quote_sign, - .set_tpm_quote_sign = _set_tpm_quote_sign, - .get_evid_sign = _get_evid_sign, - .set_evid_sign = _set_evid_sign, + .get_quote_info = _get_quote_info, + .get_evid_sig = _get_evid_sig, + .set_evid_sig = _set_evid_sig, }, .vendor_id = PEN_TCG, .type = TCG_PTS_SIMPLE_EVID_FINAL, .flags = flags, .comp_hash_algorithm = comp_hash_algorithm, .pcr_comp = pcr_comp, - .tpm_quote_sign = tpm_quote_sign, - .evid_sign = evid_sign, + .tpm_quote_sig = tpm_quote_sig, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -408,24 +377,17 @@ pa_tnc_attr_t *tcg_pts_attr_simple_evid_final_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, - .get_flags= _get_flags, - .set_flags= _set_flags, - .get_comp_hash_algorithm = _get_comp_hash_algorithm, - .set_comp_hash_algorithm = _set_comp_hash_algorithm, - .get_comp_pcr_len = _get_comp_pcr_len, - .get_pcr_comp = _get_pcr_comp, - .set_pcr_comp = _set_pcr_comp, - .get_tpm_quote_sign_len = _get_tpm_quote_sign_len, - .get_tpm_quote_sign = _get_tpm_quote_sign, - .set_tpm_quote_sign = _set_tpm_quote_sign, - .get_evid_sign = _get_evid_sign, - .set_evid_sign = _set_evid_sign, + .get_quote_info = _get_quote_info, + .get_evid_sig = _get_evid_sig, + .set_evid_sig = _set_evid_sig, }, .vendor_id = PEN_TCG, .type = TCG_PTS_SIMPLE_EVID_FINAL, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_simple_evid_final.h b/src/libpts/tcg/tcg_pts_attr_simple_evid_final.h index 351981921..3d98bfce7 100644 --- a/src/libpts/tcg/tcg_pts_attr_simple_evid_final.h +++ b/src/libpts/tcg/tcg_pts_attr_simple_evid_final.h @@ -22,32 +22,12 @@ #define TCG_PTS_ATTR_SIMPLE_EVID_FINAL_H_ typedef struct tcg_pts_attr_simple_evid_final_t tcg_pts_attr_simple_evid_final_t; -typedef enum pts_simple_evid_final_flag_t pts_simple_evid_final_flag_t; #include "tcg_attr.h" #include "tcg_pts_attr_meas_algo.h" #include "pa_tnc/pa_tnc_attr.h" /** - * PTS Simple Evidence Final Flags - */ -enum pts_simple_evid_final_flag_t { - /** No Optional TPM PCR Composite nor Optional TPM Quote Signature fields included */ - PTS_SIMPLE_EVID_FINAL_FLAG_NO = 0, - /** Optional TPM PCR Composite and Optional TPM Quote Signature fields included */ - /** using TPM_QUOTE_INFO */ - PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO = 1, - /** Optional TPM PCR Composite and Optional TPM Quote Signature fields included */ - /** using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO was not appended */ - PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2 = 2, - /** Optional TPM PCR Composite and Optional TPM Quote Signature fields included */ - /** using TPM_QUOTE_INFO2, TPM_CAP_VERSION_INFO was appended */ - PTS_SIMPLE_EVID_FINAL_FLAG_TPM_QUOTE_INFO2_CAP_VER = 3, - /** Optional Evidence Signature included */ - PTS_SIMPLE_EVID_FINAL_FLAG_EVID = 4, -}; - -/** * Class implementing the TCG PTS Simple Evidence Final attribute * */ @@ -57,112 +37,49 @@ struct tcg_pts_attr_simple_evid_final_t { * Public PA-TNC attribute interface */ pa_tnc_attr_t pa_tnc_attribute; - - /** - * Get flags for PTS Simple Evidence Final - * - * @return Set of flags - */ - pts_simple_evid_final_flag_t (*get_flags)(tcg_pts_attr_simple_evid_final_t *this); /** - * Set flags for PTS Simple Evidence Final - * - * @param flags Set of flags - */ - void (*set_flags)(tcg_pts_attr_simple_evid_final_t *this, - pts_simple_evid_final_flag_t flags); - - /** - * Get Optional Composite Hash Algorithm - * - * @return Composite Hash Algorithm - */ - pts_meas_algorithms_t (*get_comp_hash_algorithm)(tcg_pts_attr_simple_evid_final_t *this); - - /** - * Set Optional Composite Hash Algorithm + * Get Optional PCR Composite and TPM Quote Signature * - * @param hash_algorithm Composite Hash Algorithm + * @param comp_hash_algo Optional Composite Hash Algorithm + * @param pcr_comp Optional PCR Composite + * @param tpm_quote sig Optional TPM Quote Signature + * @return PTS_SIMPLE_EVID_FINAL flags */ - void (*set_comp_hash_algorithm)(tcg_pts_attr_simple_evid_final_t *this, - pts_meas_algorithms_t hash_algorithm); - - /** - * Get Optional TPM PCR Composite Length - * - * @return Length of Composite PCR Length - */ - u_int32_t (*get_comp_pcr_len)(tcg_pts_attr_simple_evid_final_t *this); - - /** - * Get Optional TPM PCR Composite - * - * @return PCR Composite - */ - chunk_t (*get_pcr_comp)(tcg_pts_attr_simple_evid_final_t *this); - - /** - * Set Optional TPM PCR Composite - * - * @param pcr_comp PCR Composite - */ - void (*set_pcr_comp)(tcg_pts_attr_simple_evid_final_t *this, - chunk_t pcr_comp); - - /** - * Get Optional TPM Quote Signature Length - * - * @return TPM Quote Signature Length - */ - u_int32_t (*get_tpm_quote_sign_len)(tcg_pts_attr_simple_evid_final_t *this); - - /** - * Get Optional TPM Quote Signature - * - * @return TPM Quote Signature - */ - chunk_t (*get_tpm_quote_sign)(tcg_pts_attr_simple_evid_final_t *this); - - /** - * Set Optional TPM Quote Signature - * - * @param tpm_quote_sign TPM Quote Signature - */ - void (*set_tpm_quote_sign)(tcg_pts_attr_simple_evid_final_t *this, - chunk_t tpm_quote_sign); + u_int8_t (*get_quote_info)(tcg_pts_attr_simple_evid_final_t *this, + pts_meas_algorithms_t *comp_hash_algo, + chunk_t *pcr_comp, chunk_t *tpm_quote_sig); /** * Get Optional Evidence Signature * - * @return Optional Evidence Signature + * @evid_sig Optional Evidence Signature + * @return TRUE if Evidence Signature is available */ - chunk_t (*get_evid_sign)(tcg_pts_attr_simple_evid_final_t *this); - + bool (*get_evid_sig)(tcg_pts_attr_simple_evid_final_t *this, chunk_t *evid_sig); + /** * Set Optional Evidence Signature * - * @param signature Optional Evidence Signature + * @evid_sig Optional Evidence Signature */ - void (*set_evid_sign)(tcg_pts_attr_simple_evid_final_t *this, - chunk_t signature); + void (*set_evid_sig)(tcg_pts_attr_simple_evid_final_t *this, chunk_t evid_sig); }; /** * Creates an tcg_pts_attr_simple_evid_final_t object - * + * * @param flags Set of flags * @param comp_hash_algorithm Composite Hash Algorithm * @param pcr_comp Optional TPM PCR Composite * @param tpm_quote_sign Optional TPM Quote Signature - * @param evid_sign Optional Evidence Signature */ -pa_tnc_attr_t* tcg_pts_attr_simple_evid_final_create(pts_simple_evid_final_flag_t flags, +pa_tnc_attr_t* tcg_pts_attr_simple_evid_final_create( + u_int8_t flags, pts_meas_algorithms_t comp_hash_algorithm, chunk_t pcr_comp, - chunk_t tpm_quote_sign, - chunk_t evid_sign); + chunk_t tpm_quote_sign); /** * Creates an tcg_pts_attr_simple_evid_final_t object from received data diff --git a/src/libpts/tcg/tcg_pts_attr_tpm_version_info.c b/src/libpts/tcg/tcg_pts_attr_tpm_version_info.c index a1e91cccf..944a12cc9 100644 --- a/src/libpts/tcg/tcg_pts_attr_tpm_version_info.c +++ b/src/libpts/tcg/tcg_pts_attr_tpm_version_info.c @@ -72,6 +72,11 @@ struct private_tcg_pts_attr_tpm_version_info_t { * TPM Version Information */ chunk_t tpm_version_info; + + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -135,12 +140,22 @@ METHOD(pa_tnc_attr_t, process, status_t, return SUCCESS; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_tpm_version_info_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_tpm_version_info_t *this) { - free(this->value.ptr); - free(this->tpm_version_info.ptr); - free(this); + if (ref_put(&this->ref)) + { + free(this->value.ptr); + free(this->tpm_version_info.ptr); + free(this); + } } METHOD(tcg_pts_attr_tpm_version_info_t, get_tpm_version_info, chunk_t, @@ -173,6 +188,7 @@ pa_tnc_attr_t *tcg_pts_attr_tpm_version_info_create(chunk_t tpm_version_info) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_tpm_version_info = _get_tpm_version_info, @@ -180,7 +196,8 @@ pa_tnc_attr_t *tcg_pts_attr_tpm_version_info_create(chunk_t tpm_version_info) }, .vendor_id = PEN_TCG, .type = TCG_PTS_TPM_VERSION_INFO, - .tpm_version_info = tpm_version_info, + .tpm_version_info = chunk_clone(tpm_version_info), + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -204,6 +221,7 @@ pa_tnc_attr_t *tcg_pts_attr_tpm_version_info_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_tpm_version_info = _get_tpm_version_info, @@ -212,6 +230,7 @@ pa_tnc_attr_t *tcg_pts_attr_tpm_version_info_create_from_data(chunk_t data) .vendor_id = PEN_TCG, .type = TCG_PTS_TPM_VERSION_INFO, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libpts/tcg/tcg_pts_attr_unix_file_meta.c b/src/libpts/tcg/tcg_pts_attr_unix_file_meta.c index 0f644fdf9..a9f4a115d 100644 --- a/src/libpts/tcg/tcg_pts_attr_unix_file_meta.c +++ b/src/libpts/tcg/tcg_pts_attr_unix_file_meta.c @@ -67,6 +67,7 @@ typedef struct private_tcg_pts_attr_file_meta_t private_tcg_pts_attr_file_meta_t #define PTS_FILE_META_SIZE 8 #define PTS_FILE_MEAS_RESERVED 0x00 +#define PTS_FILE_METADATA_SIZE 52 /** * Private data of an tcg_pts_attr_file_meta_t object. @@ -103,6 +104,10 @@ struct private_tcg_pts_attr_file_meta_t { */ pts_file_meta_t *metadata; + /** + * Reference count + */ + refcount_t ref; }; METHOD(pa_tnc_attr_t, get_vendor_id, pen_t, @@ -140,52 +145,29 @@ METHOD(pa_tnc_attr_t, build, void, { bio_writer_t *writer; enumerator_t *enumerator; + pts_file_metadata_t *entry; u_int64_t number_of_files; - char *filename; - u_int16_t meta_length; - pts_file_type_t type; - u_int64_t filesize; - time_t create_time; - time_t last_modify_time; - time_t last_access_time; - u_int64_t owner_id; - u_int64_t group_id; number_of_files = this->metadata->get_file_count(this->metadata); writer = bio_writer_create(PTS_FILE_META_SIZE); - /* Write the 64 bit integer field - number of files as two 32 bit parts */ - writer->write_uint32(writer, number_of_files >> 32); - writer->write_uint32(writer, number_of_files & 0xffffffff); + writer->write_uint64(writer, number_of_files); enumerator = this->metadata->create_enumerator(this->metadata); - while (enumerator->enumerate(enumerator, &filename, &meta_length, &type, - &filesize, &filesize, &create_time, &last_modify_time, &last_access_time, - &owner_id, &group_id)) + while (enumerator->enumerate(enumerator, &entry)) { - u_int64_t create_time64 = (u_int64_t)create_time; - u_int64_t modify_time64 = (u_int64_t)last_modify_time; - u_int64_t access_time64 = (u_int64_t)last_access_time; - - writer->write_uint16(writer, PTS_FILE_METADATA_SIZE + strlen(filename)); - writer->write_uint8 (writer, type); + writer->write_uint16(writer, PTS_FILE_METADATA_SIZE + + strlen(entry->filename)); + writer->write_uint8 (writer, entry->type); writer->write_uint8 (writer, PTS_FILE_MEAS_RESERVED); - - /* Write the 64 bit integer fields as two 32 bit parts */ - writer->write_uint32(writer, filesize >> 32); - writer->write_uint32(writer, filesize & 0xffffffff); - writer->write_uint32(writer, create_time64 >> 32); - writer->write_uint32(writer, create_time64 & 0xffffffff); - writer->write_uint32(writer, modify_time64 >> 32); - writer->write_uint32(writer, modify_time64 & 0xffffffff); - writer->write_uint32(writer, access_time64 >> 32); - writer->write_uint32(writer, access_time64 & 0xffffffff); - writer->write_uint32(writer, owner_id >> 32); - writer->write_uint32(writer, owner_id & 0xffffffff); - writer->write_uint32(writer, group_id >> 32); - writer->write_uint32(writer, group_id & 0xffffffff); - - writer->write_data (writer, chunk_create(filename, strlen(filename))); + writer->write_uint64(writer, entry->filesize); + writer->write_uint64(writer, entry->created); + writer->write_uint64(writer, entry->modified); + writer->write_uint64(writer, entry->accessed); + writer->write_uint64(writer, entry->owner); + writer->write_uint64(writer, entry->group); + writer->write_data (writer, chunk_create(entry->filename, + strlen(entry->filename))); } enumerator->destroy(enumerator); @@ -197,36 +179,12 @@ METHOD(pa_tnc_attr_t, process, status_t, private_tcg_pts_attr_file_meta_t *this, u_int32_t *offset) { bio_reader_t *reader; - int number_of_files; - u_int32_t number_of_files32; - - u_int16_t meta_length; - pts_file_type_t type; - u_int8_t type8; - u_int8_t reserved; - - int filesize; - u_int32_t filesize32; - - int create_time; - u_int32_t create_time32; - time_t create_time_t; - int modify_time; - u_int32_t modify_time32; - time_t modify_time_t; - int access_time; - u_int32_t access_time32; - time_t access_time_t; - - int owner_id; - u_int32_t owner_id32; - - int group_id; - u_int32_t group_id32; - - size_t len; + pts_file_metadata_t *entry; + u_int8_t type, reserved; + u_int16_t len; + u_int64_t number_of_files, filesize, created, modified, accessed; + u_int64_t owner, group; chunk_t filename; - char buf[BUF_LEN]; status_t status = FAILED; if (this->value.len < PTS_FILE_META_SIZE) @@ -236,124 +194,76 @@ METHOD(pa_tnc_attr_t, process, status_t, return FAILED; } reader = bio_reader_create(this->value); + reader->read_uint64(reader, &number_of_files); - reader->read_uint32(reader, &number_of_files32); - number_of_files = (sizeof(number_of_files) > 4) ? number_of_files32 << 32 : 0; - reader->read_uint32(reader, &number_of_files32); - number_of_files += number_of_files32; - this->metadata = pts_file_meta_create(); while (number_of_files--) { - if (!reader->read_uint16(reader, &meta_length)) + if (!reader->read_uint16(reader, &len)) { DBG1(DBG_TNC, "insufficient data for PTS file metadata length"); goto end; } - if (!reader->read_uint8 (reader, &type8)) + if (!reader->read_uint8(reader, &type)) { DBG1(DBG_TNC, "insufficient data for file type"); goto end; } - type = (pts_file_type_t)type8; - if (!reader->read_uint8 (reader, &reserved)) + if (!reader->read_uint8(reader, &reserved)) { DBG1(DBG_TNC, "insufficient data for reserved field"); goto end; } - if (!reader->read_uint32(reader, &filesize32)) - { - DBG1(DBG_TNC, "insufficient data for file size"); - goto end; - } - filesize = (sizeof(filesize) > 4) ? filesize32 << 32 : 0; - if (!reader->read_uint32(reader, &filesize32)) - { - DBG1(DBG_TNC, "insufficient data for file size"); - goto end; - } - filesize += filesize32; - - if (!reader->read_uint32(reader, &create_time32)) - { - DBG1(DBG_TNC, "insufficient data for file size"); - goto end; - } - create_time = (sizeof(create_time) > 4) ? create_time32 << 32 : 0; - if (!reader->read_uint32(reader, &create_time32)) - { - DBG1(DBG_TNC, "insufficient data for file size"); - goto end; - } - create_time += create_time32; - create_time_t = (time_t)create_time; - - if (!reader->read_uint32(reader, &modify_time32)) + if (!reader->read_uint64(reader, &filesize)) { DBG1(DBG_TNC, "insufficient data for file size"); goto end; } - modify_time = (sizeof(modify_time) > 4) ? modify_time32 << 32 : 0; - if (!reader->read_uint32(reader, &modify_time32)) + if (!reader->read_uint64(reader, &created)) { - DBG1(DBG_TNC, "insufficient data for file size"); + DBG1(DBG_TNC, "insufficient data for file create time"); goto end; } - modify_time += modify_time32; - modify_time_t = (time_t)modify_time; - - if (!reader->read_uint32(reader, &access_time32)) + if (!reader->read_uint64(reader, &modified)) { - DBG1(DBG_TNC, "insufficient data for file size"); + DBG1(DBG_TNC, "insufficient data for last modify time"); goto end; } - access_time = (sizeof(access_time) > 4) ? access_time32 << 32 : 0; - if (!reader->read_uint32(reader, &access_time32)) + if (!reader->read_uint64(reader, &accessed)) { - DBG1(DBG_TNC, "insufficient data for file size"); + DBG1(DBG_TNC, "insufficient data for last access time"); goto end; } - access_time += access_time32; - access_time_t = (time_t)access_time; - - if (!reader->read_uint32(reader, &owner_id32)) + if (!reader->read_uint64(reader, &owner)) { - DBG1(DBG_TNC, "insufficient data for file size"); + DBG1(DBG_TNC, "insufficient data for owner id"); goto end; } - owner_id = (sizeof(owner_id) > 4) ? owner_id32 << 32 : 0; - if (!reader->read_uint32(reader, &owner_id32)) + if (!reader->read_uint64(reader, &group)) { - DBG1(DBG_TNC, "insufficient data for file size"); + DBG1(DBG_TNC, "insufficient data for group id"); goto end; } - owner_id += owner_id32; - - if (!reader->read_uint32(reader, &group_id32)) - { - DBG1(DBG_TNC, "insufficient data for file size"); - goto end; - } - group_id = (sizeof(group_id) > 4) ? group_id32 << 32 : 0; - if (!reader->read_uint32(reader, &group_id32)) - { - DBG1(DBG_TNC, "insufficient data for file size"); - goto end; - } - group_id += group_id32; - - if (!reader->read_data(reader, meta_length - PTS_FILE_METADATA_SIZE, &filename)) + if (!reader->read_data(reader, len - PTS_FILE_METADATA_SIZE, &filename)) { DBG1(DBG_TNC, "insufficient data for filename"); goto end; } - len = min(filename.len, BUF_LEN-1); - memcpy(buf, filename.ptr, len); - buf[len] = '\0'; - this->metadata->add(this->metadata, buf, type, filesize, create_time_t, - modify_time_t, access_time_t, owner_id, group_id); + entry = malloc_thing(pts_file_metadata_t); + entry->type = type; + entry->filesize = filesize; + entry->created = created; + entry->modified = modified; + entry->accessed = accessed; + entry->owner = owner; + entry->group = group; + entry->filename = malloc(filename.len + 1); + entry->filename[filename.len] = '\0'; + memcpy(entry->filename, filename.ptr, filename.len); + + this->metadata->add(this->metadata, entry); } status = SUCCESS; @@ -362,12 +272,22 @@ end: return status; } +METHOD(pa_tnc_attr_t, get_ref, pa_tnc_attr_t*, + private_tcg_pts_attr_file_meta_t *this) +{ + ref_get(&this->ref); + return &this->public.pa_tnc_attribute; +} + METHOD(pa_tnc_attr_t, destroy, void, private_tcg_pts_attr_file_meta_t *this) { - this->metadata->destroy(this->metadata); - free(this->value.ptr); - free(this); + if (ref_put(&this->ref)) + { + this->metadata->destroy(this->metadata); + free(this->value.ptr); + free(this); + } } METHOD(tcg_pts_attr_file_meta_t, get_metadata, pts_file_meta_t*, @@ -393,6 +313,7 @@ pa_tnc_attr_t *tcg_pts_attr_unix_file_meta_create(pts_file_meta_t *metadata) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_metadata = _get_metadata, @@ -400,6 +321,7 @@ pa_tnc_attr_t *tcg_pts_attr_unix_file_meta_create(pts_file_meta_t *metadata) .vendor_id = PEN_TCG, .type = TCG_PTS_UNIX_FILE_META, .metadata = metadata, + .ref = 1, ); return &this->public.pa_tnc_attribute; @@ -423,6 +345,7 @@ pa_tnc_attr_t *tcg_pts_attr_unix_file_meta_create_from_data(chunk_t data) .set_noskip_flag = _set_noskip_flag, .build = _build, .process = _process, + .get_ref = _get_ref, .destroy = _destroy, }, .get_metadata = _get_metadata, @@ -430,6 +353,7 @@ pa_tnc_attr_t *tcg_pts_attr_unix_file_meta_create_from_data(chunk_t data) .vendor_id = PEN_TCG, .type = TCG_PTS_UNIX_FILE_META, .value = chunk_clone(data), + .ref = 1, ); return &this->public.pa_tnc_attribute; diff --git a/src/libradius/Makefile.am b/src/libradius/Makefile.am new file mode 100644 index 000000000..5672f7b84 --- /dev/null +++ b/src/libradius/Makefile.am @@ -0,0 +1,11 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +ipseclib_LTLIBRARIES = libradius.la +libradius_la_SOURCES = \ + radius_message.h radius_message.c \ + radius_socket.h radius_socket.c \ + radius_client.h radius_client.c \ + radius_config.h radius_config.c \ + radius_mppe.h + diff --git a/src/libcharon/plugins/eap_radius/radius_client.c b/src/libradius/radius_client.c index 245308e59..acdac78c9 100644 --- a/src/libcharon/plugins/eap_radius/radius_client.c +++ b/src/libradius/radius_client.c @@ -14,14 +14,12 @@ */ #include "radius_client.h" - -#include "eap_radius_plugin.h" -#include "radius_server.h" +#include "radius_config.h" #include <unistd.h> #include <errno.h> -#include <daemon.h> +#include <debug.h> #include <utils/host.h> #include <utils/linked_list.h> #include <threading/condvar.h> @@ -40,9 +38,9 @@ struct private_radius_client_t { radius_client_t public; /** - * Selected RADIUS server + * Selected RADIUS server configuration */ - radius_server_t *server; + radius_config_t *config; /** * RADIUS servers State attribute @@ -86,37 +84,41 @@ METHOD(radius_client_t, request, radius_message_t*, char virtual[] = {0x00,0x00,0x00,0x05}; radius_socket_t *socket; radius_message_t *res; + chunk_t data; /* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */ req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual))); /* add our NAS-Identifier */ req->add(req, RAT_NAS_IDENTIFIER, - this->server->get_nas_identifier(this->server)); + this->config->get_nas_identifier(this->config)); /* add State attribute, if server sent one */ if (this->state.ptr) { req->add(req, RAT_STATE, this->state); } - socket = this->server->get_socket(this->server); + socket = this->config->get_socket(this->config); DBG1(DBG_CFG, "sending RADIUS %N to server '%s'", radius_message_code_names, - req->get_code(req), this->server->get_name(this->server)); + req->get_code(req), this->config->get_name(this->config)); + res = socket->request(socket, req); if (res) { DBG1(DBG_CFG, "received RADIUS %N from server '%s'", radius_message_code_names, res->get_code(res), - this->server->get_name(this->server)); + this->config->get_name(this->config)); + data = res->get_encoding(res); + DBG3(DBG_CFG, "%B", &data); + save_state(this, res); if (res->get_code(res) == RMC_ACCESS_ACCEPT) { chunk_clear(&this->msk); this->msk = socket->decrypt_msk(socket, req, res); } - this->server->put_socket(this->server, socket, TRUE); + this->config->put_socket(this->config, socket, TRUE); return res; } - this->server->put_socket(this->server, socket, FALSE); - charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING); + this->config->put_socket(this->config, socket, FALSE); return NULL; } @@ -129,7 +131,7 @@ METHOD(radius_client_t, get_msk, chunk_t, METHOD(radius_client_t, destroy, void, private_radius_client_t *this) { - this->server->destroy(this->server); + this->config->destroy(this->config); chunk_clear(&this->msk); free(this->state.ptr); free(this); @@ -138,12 +140,9 @@ METHOD(radius_client_t, destroy, void, /** * See header */ -radius_client_t *radius_client_create() +radius_client_t *radius_client_create(radius_config_t *config) { private_radius_client_t *this; - enumerator_t *enumerator; - radius_server_t *server; - int current, best = -1; INIT(this, .public = { @@ -151,36 +150,8 @@ radius_client_t *radius_client_create() .get_msk = _get_msk, .destroy = _destroy, }, + .config = config, ); - enumerator = eap_radius_create_server_enumerator(); - while (enumerator->enumerate(enumerator, &server)) - { - current = server->get_preference(server); - if (current > best || - /* for two with equal preference, 50-50 chance */ - (current == best && random() % 2 == 0)) - { - DBG2(DBG_CFG, "RADIUS server '%s' is candidate: %d", - server->get_name(server), current); - best = current; - DESTROY_IF(this->server); - this->server = server->get_ref(server); - } - else - { - DBG2(DBG_CFG, "RADIUS server '%s' skipped: %d", - server->get_name(server), current); - } - } - enumerator->destroy(enumerator); - - if (!this->server) - { - free(this); - return NULL; - } - return &this->public; } - diff --git a/src/libcharon/plugins/eap_radius/radius_client.h b/src/libradius/radius_client.h index e4f3a7222..cf5f79b6c 100644 --- a/src/libcharon/plugins/eap_radius/radius_client.h +++ b/src/libradius/radius_client.h @@ -15,13 +15,14 @@ /** * @defgroup radius_client radius_client - * @{ @ingroup eap_radius + * @{ @ingroup libradius */ #ifndef RADIUS_CLIENT_H_ #define RADIUS_CLIENT_H_ #include "radius_message.h" +#include "radius_config.h" typedef struct radius_client_t radius_client_t; @@ -59,8 +60,9 @@ struct radius_client_t { /** * Create a RADIUS client. * + * @param config reference to a server configuration, gets owned * @return radius_client_t object */ -radius_client_t *radius_client_create(); +radius_client_t *radius_client_create(radius_config_t *config); #endif /** RADIUS_CLIENT_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_server.c b/src/libradius/radius_config.c index 3baf39807..6e3394bb0 100644 --- a/src/libcharon/plugins/eap_radius/radius_server.c +++ b/src/libradius/radius_config.c @@ -13,23 +13,23 @@ * for more details. */ -#include "radius_server.h" +#include "radius_config.h" #include <threading/mutex.h> #include <threading/condvar.h> #include <utils/linked_list.h> -typedef struct private_radius_server_t private_radius_server_t; +typedef struct private_radius_config_t private_radius_config_t; /** - * Private data of an radius_server_t object. + * Private data of an radius_config_t object. */ -struct private_radius_server_t { +struct private_radius_config_t { /** - * Public radius_server_t interface. + * Public radius_config_t interface. */ - radius_server_t public; + radius_config_t public; /** * list of radius sockets, as radius_socket_t @@ -82,8 +82,8 @@ struct private_radius_server_t { refcount_t ref; }; -METHOD(radius_server_t, get_socket, radius_socket_t*, - private_radius_server_t *this) +METHOD(radius_config_t, get_socket, radius_socket_t*, + private_radius_config_t *this) { radius_socket_t *skt; @@ -96,8 +96,8 @@ METHOD(radius_server_t, get_socket, radius_socket_t*, return skt; } -METHOD(radius_server_t, put_socket, void, - private_radius_server_t *this, radius_socket_t *skt, bool result) +METHOD(radius_config_t, put_socket, void, + private_radius_config_t *this, radius_socket_t *skt, bool result) { this->mutex->lock(this->mutex); this->sockets->insert_last(this->sockets, skt); @@ -106,14 +106,14 @@ METHOD(radius_server_t, put_socket, void, this->reachable = result; } -METHOD(radius_server_t, get_nas_identifier, chunk_t, - private_radius_server_t *this) +METHOD(radius_config_t, get_nas_identifier, chunk_t, + private_radius_config_t *this) { return this->nas_identifier; } -METHOD(radius_server_t, get_preference, int, - private_radius_server_t *this) +METHOD(radius_config_t, get_preference, int, + private_radius_config_t *this) { int pref; @@ -147,22 +147,22 @@ METHOD(radius_server_t, get_preference, int, return pref; } -METHOD(radius_server_t, get_name, char*, - private_radius_server_t *this) +METHOD(radius_config_t, get_name, char*, + private_radius_config_t *this) { return this->name; } -METHOD(radius_server_t, get_ref, radius_server_t*, - private_radius_server_t *this) +METHOD(radius_config_t, get_ref, radius_config_t*, + private_radius_config_t *this) { ref_get(&this->ref); return &this->public; } -METHOD(radius_server_t, destroy, void, - private_radius_server_t *this) +METHOD(radius_config_t, destroy, void, + private_radius_config_t *this) { if (ref_put(&this->ref)) { @@ -177,10 +177,12 @@ METHOD(radius_server_t, destroy, void, /** * See header */ -radius_server_t *radius_server_create(char *name, char *address, u_int16_t port, - char *nas_identifier, char *secret, int sockets, int preference) +radius_config_t *radius_config_create(char *name, char *address, + u_int16_t auth_port, u_int16_t acct_port, + char *nas_identifier, char *secret, + int sockets, int preference) { - private_radius_server_t *this; + private_radius_config_t *this; radius_socket_t *socket; INIT(this, @@ -206,7 +208,7 @@ radius_server_t *radius_server_create(char *name, char *address, u_int16_t port, while (sockets--) { - socket = radius_socket_create(address, port, + socket = radius_socket_create(address, auth_port, acct_port, chunk_create(secret, strlen(secret))); if (!socket) { diff --git a/src/libcharon/plugins/eap_radius/radius_server.h b/src/libradius/radius_config.h index c59361c49..40ed6197a 100644 --- a/src/libcharon/plugins/eap_radius/radius_server.h +++ b/src/libradius/radius_config.h @@ -14,28 +14,28 @@ */ /** - * @defgroup radius_server radius_server - * @{ @ingroup eap_radius + * @defgroup radius_config radius_config + * @{ @ingroup libradius */ -#ifndef RADIUS_SERVER_H_ -#define RADIUS_SERVER_H_ +#ifndef RADIUS_CONFIG_H_ +#define RADIUS_CONFIG_H_ -typedef struct radius_server_t radius_server_t; +typedef struct radius_config_t radius_config_t; #include "radius_socket.h" /** * RADIUS server configuration. */ -struct radius_server_t { +struct radius_config_t { /** - * Get a RADIUS socket from the pool to communicate with this server. + * Get a RADIUS socket from the pool to communicate with this config. * * @return RADIUS socket */ - radius_socket_t* (*get_socket)(radius_server_t *this); + radius_socket_t* (*get_socket)(radius_config_t *this); /** * Release a socket to the pool after use. @@ -43,14 +43,14 @@ struct radius_server_t { * @param skt RADIUS socket to release * @param result result of the socket use, TRUE for success */ - void (*put_socket)(radius_server_t *this, radius_socket_t *skt, bool result); + void (*put_socket)(radius_config_t *this, radius_socket_t *skt, bool result); /** * Get the NAS-Identifier to use with this server. * * @return NAS-Identifier, internal data */ - chunk_t (*get_nas_identifier)(radius_server_t *this); + chunk_t (*get_nas_identifier)(radius_config_t *this); /** * Get the preference of this server. @@ -58,40 +58,43 @@ struct radius_server_t { * Based on the available sockets and the server reachability a preference * value is calculated: better servers return a higher value. */ - int (*get_preference)(radius_server_t *this); + int (*get_preference)(radius_config_t *this); /** * Get the name of the RADIUS server. * * @return server name */ - char* (*get_name)(radius_server_t *this); + char* (*get_name)(radius_config_t *this); /** - * Increase reference count of this server. + * Increase reference count of this server configuration. * * @return this */ - radius_server_t* (*get_ref)(radius_server_t *this); + radius_config_t* (*get_ref)(radius_config_t *this); /** - * Destroy a radius_server_t. + * Destroy a radius_config_t. */ - void (*destroy)(radius_server_t *this); + void (*destroy)(radius_config_t *this); }; /** - * Create a radius_server instance. + * Create a radius_config_t instance. * * @param name server name * @param address server address - * @param port server port + * @param auth_port server port for authentication + * @param acct_port server port for accounting * @param nas_identifier NAS-Identifier to use with this server * @param secret secret to use with this server * @param sockets number of sockets to create in pool * @param preference preference boost for this server */ -radius_server_t *radius_server_create(char *name, char *address, u_int16_t port, - char *nas_identifier, char *secret, int sockets, int preference); +radius_config_t *radius_config_create(char *name, char *address, + u_int16_t auth_port, u_int16_t acct_port, + char *nas_identifier, char *secret, + int sockets, int preference); -#endif /** RADIUS_SERVER_H_ @}*/ +#endif /** RADIUS_CONFIG_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_message.c b/src/libradius/radius_message.c index 23a29b772..ce8903cdb 100644 --- a/src/libcharon/plugins/eap_radius/radius_message.c +++ b/src/libradius/radius_message.c @@ -15,7 +15,7 @@ #include "radius_message.h" -#include <daemon.h> +#include <debug.h> #include <crypto/hashers/hasher.h> typedef struct private_radius_message_t private_radius_message_t; @@ -74,7 +74,14 @@ ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONS "Accounting-Response"); ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE, "Access-Challenge"); -ENUM_END(radius_message_code_names, RMC_ACCESS_CHALLENGE); +ENUM_NEXT(radius_message_code_names, RMC_DISCONNECT_REQUEST, RMC_COA_NAK, RMC_ACCESS_CHALLENGE, + "Disconnect-Request", + "Disconnect-ACK", + "Disconnect-NAK", + "CoA-Request", + "CoA-ACK", + "CoA-NAK"); +ENUM_END(radius_message_code_names, RMC_COA_NAK); ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX, "User-Name", @@ -261,7 +268,7 @@ METHOD(radius_message_t, add, void, { rattr_t *attribute; - data.len = min(data.len, 253); + data.len = min(data.len, MAX_RADIUS_ATTRIBUTE_SIZE); this->msg = realloc(this->msg, ntohs(this->msg->length) + sizeof(rattr_t) + data.len); attribute = ((void*)this->msg) + ntohs(this->msg->length); @@ -272,19 +279,48 @@ METHOD(radius_message_t, add, void, } METHOD(radius_message_t, sign, void, - private_radius_message_t *this, rng_t *rng, signer_t *signer) + private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret, + hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth) { - char buf[HASH_SIZE_MD5]; + if (rng) + { + /* build Request-Authenticator */ + rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator); + } + else + { + /* prepare build of Response-Authenticator */ + if (req_auth) + { + memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5); + } + else + { + memset(this->msg->authenticator, 0, sizeof(this->msg->authenticator)); + } + } - /* build Request-Authenticator */ - rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator); + if (msg_auth) + { + char buf[HASH_SIZE_MD5]; - /* build Message-Authenticator attribute, using 16 null bytes */ - memset(buf, 0, sizeof(buf)); - add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf))); - signer->get_signature(signer, + /* build Message-Authenticator attribute, using 16 null bytes */ + memset(buf, 0, sizeof(buf)); + add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf))); + signer->get_signature(signer, chunk_create((u_char*)this->msg, ntohs(this->msg->length)), ((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5); + } + + if (!rng) + { + chunk_t msg; + + /* build Response-Authenticator */ + msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length)); + hasher->get_hash(hasher, msg, NULL); + hasher->get_hash(hasher, secret, this->msg->authenticator); + } } METHOD(radius_message_t, verify, bool, @@ -297,18 +333,29 @@ METHOD(radius_message_t, verify, bool, chunk_t data, msg; bool has_eap = FALSE, has_auth = FALSE; - /* replace Response by Request Authenticator for verification */ - memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5); - memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5); msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length)); - /* verify Response-Authenticator */ - hasher->get_hash(hasher, msg, NULL); - hasher->get_hash(hasher, secret, buf); - if (!memeq(buf, res_auth, HASH_SIZE_MD5)) + if (this->msg->code != RMC_ACCESS_REQUEST) { - DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed"); - return FALSE; + /* replace Response by Request Authenticator for verification */ + memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5); + if (req_auth) + { + memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5); + } + else + { + memset(this->msg->authenticator, 0, HASH_SIZE_MD5); + } + + /* verify Response-Authenticator */ + hasher->get_hash(hasher, msg, NULL); + hasher->get_hash(hasher, secret, buf); + if (!memeq(buf, res_auth, HASH_SIZE_MD5)) + { + DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed"); + return FALSE; + } } /* verify Message-Authenticator attribute */ @@ -346,8 +393,12 @@ METHOD(radius_message_t, verify, bool, } } enumerator->destroy(enumerator); - /* restore Response-Authenticator */ - memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5); + + if (this->msg->code != RMC_ACCESS_REQUEST) + { + /* restore Response-Authenticator */ + memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5); + } if (has_eap && !has_auth) { /* Message-Authenticator is required if we have an EAP-Message */ @@ -398,7 +449,7 @@ METHOD(radius_message_t, destroy, void, /** * Generic constructor */ -static private_radius_message_t *radius_message_create() +static private_radius_message_t *radius_message_create_empty() { private_radius_message_t *this; @@ -423,12 +474,12 @@ static private_radius_message_t *radius_message_create() /** * See header */ -radius_message_t *radius_message_create_request() +radius_message_t *radius_message_create(radius_message_code_t code) { - private_radius_message_t *this = radius_message_create(); + private_radius_message_t *this = radius_message_create_empty(); INIT(this->msg, - .code = RMC_ACCESS_REQUEST, + .code = code, .identifier = 0, .length = htons(sizeof(rmsg_t)), ); @@ -439,9 +490,9 @@ radius_message_t *radius_message_create_request() /** * See header */ -radius_message_t *radius_message_parse_response(chunk_t data) +radius_message_t *radius_message_parse(chunk_t data) { - private_radius_message_t *this = radius_message_create(); + private_radius_message_t *this = radius_message_create_empty(); this->msg = malloc(data.len); memcpy(this->msg, data.ptr, data.len); @@ -454,4 +505,3 @@ radius_message_t *radius_message_parse_response(chunk_t data) } return &this->public; } - diff --git a/src/libcharon/plugins/eap_radius/radius_message.h b/src/libradius/radius_message.h index 266839d3b..90698ae7b 100644 --- a/src/libcharon/plugins/eap_radius/radius_message.h +++ b/src/libradius/radius_message.h @@ -14,8 +14,13 @@ */ /** + * @defgroup libradius libradius + * + * @addtogroup libradius + * RADIUS protocol support library. + * * @defgroup radius_message radius_message - * @{ @ingroup eap_radius + * @{ @ingroup libradius */ #ifndef RADIUS_MESSAGE_H_ @@ -23,6 +28,10 @@ #include <library.h> +#define MAX_RADIUS_ATTRIBUTE_SIZE 253 + +#define RADIUS_TUNNEL_TYPE_ESP 9 + typedef struct radius_message_t radius_message_t; typedef enum radius_message_code_t radius_message_code_t; typedef enum radius_attribute_type_t radius_attribute_type_t; @@ -37,6 +46,12 @@ enum radius_message_code_t { RMC_ACCOUNTING_REQUEST = 4, RMC_ACCOUNTING_RESPONSE = 5, RMC_ACCESS_CHALLENGE = 11, + RMC_DISCONNECT_REQUEST = 40, + RMC_DISCONNECT_ACK = 41, + RMC_DISCONNECT_NAK = 42, + RMC_COA_REQUEST = 43, + RMC_COA_ACK = 44, + RMC_COA_NAK = 45, }; /** @@ -236,18 +251,23 @@ struct radius_message_t { /** * Calculate and add the Message-Authenticator attribute to the message. * - * @param rng RNG to create Request-Authenticator + * @param req_auth 16 byte Authenticator of request, or NULL + * @param secret shared RADIUS secret * @param signer HMAC-MD5 signer with secret set + * @param hasher MD5 hasher + * @param rng RNG to create Request-Authenticator, NULL to omit + * @param msg_auth calculate and add Message-Authenticator */ - void (*sign)(radius_message_t *this, rng_t *rng, signer_t *signer); + void (*sign)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret, + hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth); /** - * Verify the integrity of a received RADIUS response. + * Verify the integrity of a received RADIUS message. * - * @param req_auth 16 byte Authenticator of the corresponding request + * @param req_auth 16 byte Authenticator of request, or NULL * @param secret shared RADIUS secret - * @param hasher hasher to verify Response-Authenticator - * @param signer signer to verify Message-Authenticator attribute + * @param signer HMAC-MD5 signer with secret set + * @param hasher MD5 hasher */ bool (*verify)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret, hasher_t *hasher, signer_t *signer); @@ -259,18 +279,19 @@ struct radius_message_t { }; /** - * Create an empty RADIUS request message (RMT_ACCESS_REQUEST). + * Create an empty RADIUS message. * + * @param code request type * @return radius_message_t object */ -radius_message_t *radius_message_create_request(); +radius_message_t *radius_message_create(radius_message_code_t code); /** - * Parse and verify a recevied RADIUS response. + * Parse and verify a recevied RADIUS message. * * @param data received message data * @return radius_message_t object, NULL if length invalid */ -radius_message_t *radius_message_parse_response(chunk_t data); +radius_message_t *radius_message_parse(chunk_t data); #endif /** RADIUS_MESSAGE_H_ @}*/ diff --git a/src/libradius/radius_mppe.h b/src/libradius/radius_mppe.h new file mode 100644 index 000000000..1b7a732ec --- /dev/null +++ b/src/libradius/radius_mppe.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 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 radius_mppe radius_mppe + * @{ @ingroup libradius + */ + +#ifndef RADIUS_MPPE_H_ +#define RADIUS_MPPE_H_ + +/** + * Microsoft specific vendor attributes + */ +#define MS_MPPE_SEND_KEY 16 +#define MS_MPPE_RECV_KEY 17 + +typedef struct mppe_key_t mppe_key_t; + +struct mppe_key_t { + u_int32_t id; + u_int8_t type; + u_int8_t length; + u_int16_t salt; + u_int8_t key[]; +} __attribute__((packed)); + +#endif /** RADIUS_MPPE_H_ @}*/ diff --git a/src/libcharon/plugins/eap_radius/radius_socket.c b/src/libradius/radius_socket.c index b3229c288..048c8814e 100644 --- a/src/libcharon/plugins/eap_radius/radius_socket.c +++ b/src/libradius/radius_socket.c @@ -14,23 +14,14 @@ */ #include "radius_socket.h" +#include "radius_mppe.h" #include <errno.h> #include <unistd.h> +#include <pen/pen.h> #include <debug.h> -/** - * Vendor-Id of Microsoft specific attributes - */ -#define VENDOR_ID_MICROSOFT 311 - -/** - * Microsoft specific vendor attributes - */ -#define MS_MPPE_SEND_KEY 16 -#define MS_MPPE_RECV_KEY 17 - typedef struct private_radius_socket_t private_radius_socket_t; /** @@ -44,19 +35,29 @@ struct private_radius_socket_t { radius_socket_t public; /** - * socket file descriptor + * Server port for authentication */ - int fd; + u_int16_t auth_port; /** - * Server address + * socket file descriptor for authentication */ - char *address; + int auth_fd; /** - * Server port + * Server port for accounting */ - u_int16_t port; + u_int16_t acct_port; + + /** + * socket file descriptor for accounting + */ + int acct_fd; + + /** + * Server address + */ + char *address; /** * current RADIUS identifier @@ -87,35 +88,36 @@ struct private_radius_socket_t { /** * Check or establish RADIUS connection */ -static bool check_connection(private_radius_socket_t *this) +static bool check_connection(private_radius_socket_t *this, + int *fd, u_int16_t port) { - if (this->fd == -1) + if (*fd == -1) { host_t *server; - server = host_create_from_dns(this->address, AF_UNSPEC, this->port); + server = host_create_from_dns(this->address, AF_UNSPEC, port); if (!server) { DBG1(DBG_CFG, "resolving RADIUS server address '%s' failed", this->address); return FALSE; } - this->fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP); - if (this->fd == -1) + *fd = socket(server->get_family(server), SOCK_DGRAM, IPPROTO_UDP); + if (*fd == -1) { DBG1(DBG_CFG, "opening RADIUS socket for %#H failed: %s", server, strerror(errno)); server->destroy(server); return FALSE; } - if (connect(this->fd, server->get_sockaddr(server), + if (connect(*fd, server->get_sockaddr(server), *server->get_sockaddr_len(server)) < 0) { DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s", server, strerror(errno)); server->destroy(server); - close(this->fd); - this->fd = -1; + close(*fd); + *fd = -1; return FALSE; } server->destroy(server); @@ -127,19 +129,36 @@ METHOD(radius_socket_t, request, radius_message_t*, private_radius_socket_t *this, radius_message_t *request) { chunk_t data; - int i; + int i, *fd; + u_int16_t port; + rng_t *rng = NULL; + + if (request->get_code(request) == RMC_ACCOUNTING_REQUEST) + { + fd = &this->acct_fd; + port = this->acct_port; + } + else + { + fd = &this->auth_fd; + port = this->auth_port; + rng = this->rng; + } /* set Message Identifier */ request->set_identifier(request, this->identifier++); /* sign the request */ - request->sign(request, this->rng, this->signer); + request->sign(request, NULL, this->secret, this->hasher, this->signer, + rng, rng != NULL); - if (!check_connection(this)) + if (!check_connection(this, fd, port)) { return NULL; } data = request->get_encoding(request); + DBG3(DBG_CFG, "%B", &data); + /* timeout after 2, 3, 4, 5 seconds */ for (i = 2; i <= 5; i++) { @@ -150,7 +169,7 @@ METHOD(radius_socket_t, request, radius_message_t*, fd_set fds; int res; - if (send(this->fd, data.ptr, data.len, 0) != data.len) + if (send(*fd, data.ptr, data.len, 0) != data.len) { DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno)); return NULL; @@ -161,8 +180,8 @@ METHOD(radius_socket_t, request, radius_message_t*, while (TRUE) { FD_ZERO(&fds); - FD_SET(this->fd, &fds); - res = select(this->fd + 1, &fds, NULL, NULL, &tv); + FD_SET(*fd, &fds); + res = select((*fd) + 1, &fds, NULL, NULL, &tv); /* TODO: updated tv to time not waited. Linux does this for us. */ if (res < 0) { /* failed */ @@ -176,14 +195,14 @@ METHOD(radius_socket_t, request, radius_message_t*, retransmit = TRUE; break; } - res = recv(this->fd, buf, sizeof(buf), MSG_DONTWAIT); + res = recv(*fd, buf, sizeof(buf), MSG_DONTWAIT); if (res <= 0) { DBG1(DBG_CFG, "receiving RADIUS message failed: %s", strerror(errno)); break; } - response = radius_message_parse_response(chunk_create(buf, res)); + response = radius_message_parse(chunk_create(buf, res)); if (response) { if (response->verify(response, @@ -262,13 +281,7 @@ METHOD(radius_socket_t, decrypt_msk, chunk_t, private_radius_socket_t *this, radius_message_t *request, radius_message_t *response) { - struct { - u_int32_t id; - u_int8_t type; - u_int8_t length; - u_int16_t salt; - u_int8_t key[]; - } __attribute__((packed)) *mppe_key; + mppe_key_t *mppe_key; enumerator_t *enumerator; chunk_t data, send = chunk_empty, recv = chunk_empty; int type; @@ -276,14 +289,13 @@ METHOD(radius_socket_t, decrypt_msk, chunk_t, enumerator = response->create_enumerator(response); while (enumerator->enumerate(enumerator, &type, &data)) { - if (type == RAT_VENDOR_SPECIFIC && - data.len > sizeof(*mppe_key)) + if (type == RAT_VENDOR_SPECIFIC && data.len > sizeof(mppe_key_t)) { - mppe_key = (void*)data.ptr; - if (ntohl(mppe_key->id) == VENDOR_ID_MICROSOFT && + mppe_key = (mppe_key_t*)data.ptr; + if (ntohl(mppe_key->id) == PEN_MICROSOFT && mppe_key->length == data.len - sizeof(mppe_key->id)) { - data = chunk_create(mppe_key->key, data.len - sizeof(*mppe_key)); + data = chunk_create(mppe_key->key, data.len - sizeof(mppe_key_t)); if (mppe_key->type == MS_MPPE_SEND_KEY) { send = decrypt_mppe_key(this, mppe_key->salt, data, request); @@ -311,9 +323,13 @@ METHOD(radius_socket_t, destroy, void, DESTROY_IF(this->hasher); DESTROY_IF(this->signer); DESTROY_IF(this->rng); - if (this->fd != -1) + if (this->auth_fd != -1) + { + close(this->auth_fd); + }; + if (this->acct_fd != -1) { - close(this->fd); + close(this->acct_fd); } free(this); } @@ -321,8 +337,8 @@ METHOD(radius_socket_t, destroy, void, /** * See header */ -radius_socket_t *radius_socket_create(char *address, u_int16_t port, - chunk_t secret) +radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port, + u_int16_t acct_port, chunk_t secret) { private_radius_socket_t *this; @@ -333,13 +349,15 @@ radius_socket_t *radius_socket_create(char *address, u_int16_t port, .destroy = _destroy, }, .address = address, - .port = port, - .fd = -1, + .auth_port = auth_port, + .auth_fd = -1, + .acct_port = acct_port, + .acct_fd = -1, + .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5), + .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128), + .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), ); - this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5); - this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128); - this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!this->hasher || !this->signer || !this->rng) { DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required"); diff --git a/src/libcharon/plugins/eap_radius/radius_socket.h b/src/libradius/radius_socket.h index 2875008eb..07d642c08 100644 --- a/src/libcharon/plugins/eap_radius/radius_socket.h +++ b/src/libradius/radius_socket.h @@ -15,7 +15,7 @@ /** * @defgroup radius_socket radius_socket - * @{ @ingroup eap_radius + * @{ @ingroup libradius */ #ifndef RADIUS_SOCKET_H_ @@ -67,10 +67,11 @@ struct radius_socket_t { * Create a radius_socket instance. * * @param address server name - * @param port server port + * @param auth_port server port for authentication + * @param acct_port server port for accounting * @param secret RADIUS secret */ -radius_socket_t *radius_socket_create(char *address, u_int16_t port, - chunk_t secret); +radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port, + u_int16_t acct_port, chunk_t secret); #endif /** RADIUS_SOCKET_H_ @}*/ diff --git a/src/libsimaka/simaka_manager.h b/src/libsimaka/simaka_manager.h index a0edd60b9..64a67e56c 100644 --- a/src/libsimaka/simaka_manager.h +++ b/src/libsimaka/simaka_manager.h @@ -113,7 +113,7 @@ struct simaka_manager_t { identification_t *pseudonym); /** - * Get a stored pseudonym from one of the registerd SIM cards. + * Get a stored pseudonym from one of the registered SIM cards. * * @param id permanent identity of the peer * @return associated pseudonym identity, NULL if none found @@ -134,7 +134,7 @@ struct simaka_manager_t { u_int16_t counter); /** - * Retrieve fast reauthentication parameters from one of the registerd cards. + * Retrieve fast reauthentication parameters from one of the registered cards. * * @param id permanent identity of the peer * @param mk buffer receiving master key MK diff --git a/src/libsimaka/simaka_message.c b/src/libsimaka/simaka_message.c index 969dc45ae..a5754b985 100644 --- a/src/libsimaka/simaka_message.c +++ b/src/libsimaka/simaka_message.c @@ -139,7 +139,7 @@ ENUM(simaka_client_error_names, SIM_UNABLE_TO_PROCESS, SIM_RANDS_NOT_FRESH, */ bool simaka_attribute_skippable(simaka_attribute_t attribute) { - bool skippable = !(attribute >= 0 && attribute <= 127); + bool skippable = !((int)attribute >= 0 && attribute <= 127); DBG1(DBG_LIB, "%sskippable EAP-SIM/AKA attribute %N", skippable ? "ignoring " : "found non-", diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index b6897c4c3..d33bee6c7 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -128,6 +128,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) \ LOCAL_MODULE := libstrongswan +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 284decbd9..d3c360b47 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -257,6 +257,13 @@ if MONOLITHIC endif endif +if USE_PKCS8 + SUBDIRS += plugins/pkcs8 +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/pkcs8/libstrongswan-pkcs8.la +endif +endif + if USE_PGP SUBDIRS += plugins/pgp if MONOLITHIC diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c index 4466b37a4..4cb38d126 100644 --- a/src/libstrongswan/asn1/asn1.c +++ b/src/libstrongswan/asn1/asn1.c @@ -222,7 +222,7 @@ size_t asn1_length(chunk_t *blob) if (blob->len < 2) { - DBG2(DBG_LIB, "insufficient number of octets to parse ASN.1 length"); + DBG2(DBG_ASN, "insufficient number of octets to parse ASN.1 length"); return ASN1_INVALID_LENGTH; } @@ -234,7 +234,7 @@ size_t asn1_length(chunk_t *blob) { /* single length octet */ if (n > blob->len) { - DBG2(DBG_LIB, "length is larger than remaining blob size"); + DBG2(DBG_ASN, "length is larger than remaining blob size"); return ASN1_INVALID_LENGTH; } return n; @@ -245,13 +245,13 @@ size_t asn1_length(chunk_t *blob) if (n == 0 || n > blob->len) { - DBG2(DBG_LIB, "number of length octets invalid"); + DBG2(DBG_ASN, "number of length octets invalid"); return ASN1_INVALID_LENGTH; } if (n > sizeof(len)) { - DBG2(DBG_LIB, "number of length octets is larger than limit of" + DBG2(DBG_ASN, "number of length octets is larger than limit of" " %d octets", (int)sizeof(len)); return ASN1_INVALID_LENGTH; } @@ -265,7 +265,7 @@ size_t asn1_length(chunk_t *blob) } if (len > blob->len) { - DBG2(DBG_LIB, "length is larger than remaining blob size"); + DBG2(DBG_ASN, "length is larger than remaining blob size"); return ASN1_INVALID_LENGTH; } return len; @@ -326,10 +326,10 @@ static const int tm_leap_1970 = 477; */ time_t asn1_to_time(const chunk_t *utctime, asn1_t type) { - int tm_year, tm_mon, tm_day, tm_days, tm_hour, tm_min, tm_sec; + int tm_year, tm_mon, tm_day, tm_hour, tm_min, tm_sec; int tm_leap_4, tm_leap_100, tm_leap_400, tm_leap; int tz_hour, tz_min, tz_offset; - time_t tm_secs; + time_t tm_days, tm_secs; u_char *eot = NULL; if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL) @@ -435,6 +435,11 @@ chunk_t asn1_from_time(const time_t *time, asn1_t type) struct tm t; gmtime_r(time, &t); + /* RFC 5280 says that dates through the year 2049 MUST be encoded as UTCTIME + * and dates in 2050 or later MUST be encoded as GENERALIZEDTIME. We only + * enforce the latter to avoid overflows but allow callers to force the + * encoding to GENERALIZEDTIME */ + type = (t.tm_year >= 150) ? ASN1_GENERALIZEDTIME : type; if (type == ASN1_GENERALIZEDTIME) { format = "%04d%02d%02d%02d%02d%02dZ"; @@ -443,7 +448,7 @@ chunk_t asn1_from_time(const time_t *time, asn1_t type) else /* ASN1_UTCTIME */ { format = "%02d%02d%02d%02d%02d%02dZ"; - offset = (t.tm_year < 100)? 0 : -100; + offset = (t.tm_year < 100) ? 0 : -100; } snprintf(buf, BUF_LEN, format, t.tm_year + offset, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); @@ -471,12 +476,12 @@ void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private) { break; } - DBG2(DBG_LIB, " %s", oid_str); + DBG2(DBG_ASN, " %s", oid_str); free(oid_str); } else { - DBG2(DBG_LIB, " '%s'", oid_names[oid].name); + DBG2(DBG_ASN, " '%s'", oid_names[oid].name); } return; case ASN1_UTF8STRING: @@ -484,14 +489,14 @@ void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private) case ASN1_PRINTABLESTRING: case ASN1_T61STRING: case ASN1_VISIBLESTRING: - DBG2(DBG_LIB, " '%.*s'", (int)object.len, object.ptr); + DBG2(DBG_ASN, " '%.*s'", (int)object.len, object.ptr); return; case ASN1_UTCTIME: case ASN1_GENERALIZEDTIME: { time_t time = asn1_to_time(&object, type); - DBG2(DBG_LIB, " '%T'", &time, TRUE); + DBG2(DBG_ASN, " '%T'", &time, TRUE); } return; default: @@ -499,11 +504,11 @@ void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private) } if (private) { - DBG4(DBG_LIB, "%B", &object); + DBG4(DBG_ASN, "%B", &object); } else { - DBG3(DBG_LIB, "%B", &object); + DBG3(DBG_ASN, "%B", &object); } } @@ -517,14 +522,14 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c /* an ASN.1 object must possess at least a tag and length field */ if (object->len < 2) { - DBG2(DBG_LIB, "L%d - %s: ASN.1 object smaller than 2 octets", level, + DBG2(DBG_ASN, "L%d - %s: ASN.1 object smaller than 2 octets", level, name); return FALSE; } if (*object->ptr != type) { - DBG2(DBG_LIB, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + DBG2(DBG_ASN, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", level, name, type, *object->ptr); return FALSE; } @@ -533,12 +538,12 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c if (len == ASN1_INVALID_LENGTH || object->len < len) { - DBG2(DBG_LIB, "L%d - %s: length of ASN.1 object invalid or too large", + DBG2(DBG_ASN, "L%d - %s: length of ASN.1 object invalid or too large", level, name); return FALSE; } - DBG2(DBG_LIB, "L%d - %s:", level, name); + DBG2(DBG_ASN, "L%d - %s:", level, name); asn1_debug_simple_object(*object, type, FALSE); return TRUE; } @@ -547,14 +552,20 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c * ASN.1 definition of an algorithmIdentifier */ static const asn1Object_t algorithmIdentifierObjects[] = { - { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ - { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */ - { 1, "parameters", ASN1_EOC, ASN1_RAW|ASN1_OPT }, /* 2 */ - { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ - { 0, "exit", ASN1_EOC, ASN1_EXIT } + { 0, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "algorithm", ASN1_OID, ASN1_BODY }, /* 1 */ + { 1, "parameters", ASN1_OID, ASN1_RAW|ASN1_OPT }, /* 2 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 3 */ + { 1, "parameters", ASN1_SEQUENCE, ASN1_RAW|ASN1_OPT }, /* 4 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 5 */ + { 1, "parameters", ASN1_OCTET_STRING, ASN1_RAW|ASN1_OPT }, /* 6 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } }; -#define ALGORITHM_ID_ALG 1 -#define ALGORITHM_ID_PARAMETERS 2 +#define ALGORITHM_ID_ALG 1 +#define ALGORITHM_ID_PARAMETERS_OID 2 +#define ALGORITHM_ID_PARAMETERS_SEQ 4 +#define ALGORITHM_ID_PARAMETERS_OCT 6 /* * Defined in header @@ -576,7 +587,9 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters case ALGORITHM_ID_ALG: alg = asn1_known_oid(object); break; - case ALGORITHM_ID_PARAMETERS: + case ALGORITHM_ID_PARAMETERS_OID: + case ALGORITHM_ID_PARAMETERS_SEQ: + case ALGORITHM_ID_PARAMETERS_OCT: if (parameters != NULL) { *parameters = object; @@ -606,7 +619,7 @@ bool is_asn1(chunk_t blob) tag = *blob.ptr; if (tag != ASN1_SEQUENCE && tag != ASN1_SET && tag != ASN1_OCTET_STRING) { - DBG2(DBG_LIB, " file content is not binary ASN.1"); + DBG2(DBG_ASN, " file content is not binary ASN.1"); return FALSE; } @@ -624,7 +637,7 @@ bool is_asn1(chunk_t blob) return TRUE; } - DBG2(DBG_LIB, " file size does not match ASN.1 coded length"); + DBG2(DBG_ASN, " file size does not match ASN.1 coded length"); return FALSE; } diff --git a/src/libstrongswan/asn1/asn1.h b/src/libstrongswan/asn1/asn1.h index 05a060827..15ffff62e 100644 --- a/src/libstrongswan/asn1/asn1.h +++ b/src/libstrongswan/asn1/asn1.h @@ -35,8 +35,8 @@ typedef enum { ASN1_BOOLEAN = 0x01, ASN1_INTEGER = 0x02, ASN1_BIT_STRING = 0x03, - ASN1_OCTET_STRING = 0x04, - ASN1_NULL = 0x05, + ASN1_OCTET_STRING = 0x04, + ASN1_NULL = 0x05, ASN1_OID = 0x06, ASN1_ENUMERATED = 0x0A, ASN1_UTF8STRING = 0x0C, @@ -48,7 +48,7 @@ typedef enum { ASN1_UTCTIME = 0x17, ASN1_GENERALIZEDTIME = 0x18, ASN1_GRAPHICSTRING = 0x19, - ASN1_VISIBLESTRING = 0x1A, + ASN1_VISIBLESTRING = 0x1A, ASN1_GENERALSTRING = 0x1B, ASN1_UNIVERSALSTRING = 0x1C, ASN1_BMPSTRING = 0x1E, @@ -75,7 +75,7 @@ typedef enum { ASN1_CONTEXT_C_4 = 0xA4, ASN1_CONTEXT_C_5 = 0xA5, - ASN1_INVALID = 0x100, + ASN1_INVALID = 0x100, } asn1_t; #define ASN1_INVALID_LENGTH 0xffffffff @@ -191,6 +191,8 @@ time_t asn1_to_time(const chunk_t *utctime, asn1_t type); /** * Converts time_t to an ASN.1 UTCTIME or GENERALIZEDTIME string * + * @note The type is automatically changed to GENERALIZEDTIME if needed + * * @param time time_t in UTC * @param type ASN1_UTCTIME or ASN1_GENERALIZEDTIME * @return body of an ASN.1 code time object diff --git a/src/libstrongswan/asn1/asn1_parser.c b/src/libstrongswan/asn1/asn1_parser.c index 2a7a38a52..40e11b321 100644 --- a/src/libstrongswan/asn1/asn1_parser.c +++ b/src/libstrongswan/asn1/asn1_parser.c @@ -120,7 +120,7 @@ METHOD(asn1_parser_t, iterate, bool, if ((obj.flags & ASN1_DEF) && (blob->len == 0 || *start_ptr != obj.type) ) { /* field is missing */ - DBG2(DBG_LIB, "L%d - %s:", level, obj.name); + DBG2(DBG_ASN, "L%d - %s:", level, obj.name); if (obj.type & ASN1_CONSTRUCTED) { this->line++ ; /* skip context-specific tag */ @@ -147,7 +147,7 @@ METHOD(asn1_parser_t, iterate, bool, if (blob->len < 2) { - DBG1(DBG_LIB, "L%d - %s: ASN.1 object smaller than 2 octets", + DBG1(DBG_ASN, "L%d - %s: ASN.1 object smaller than 2 octets", level, obj.name); this->success = FALSE; goto end; @@ -157,7 +157,7 @@ METHOD(asn1_parser_t, iterate, bool, if (blob1->len == ASN1_INVALID_LENGTH) { - DBG1(DBG_LIB, "L%d - %s: length of ASN.1 object invalid or too large", + DBG1(DBG_ASN, "L%d - %s: length of ASN.1 object invalid or too large", level, obj.name); this->success = FALSE; } @@ -170,7 +170,7 @@ METHOD(asn1_parser_t, iterate, bool, if (obj.flags & ASN1_RAW) { - DBG2(DBG_LIB, "L%d - %s:", level, obj.name); + DBG2(DBG_ASN, "L%d - %s:", level, obj.name); object->ptr = start_ptr; object->len = (size_t)(blob->ptr - start_ptr); goto end; @@ -178,14 +178,14 @@ METHOD(asn1_parser_t, iterate, bool, if (*start_ptr != obj.type && !(this->implicit && this->line == 0)) { - DBG1(DBG_LIB, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", + DBG2(DBG_ASN, "L%d - %s: ASN1 tag 0x%02x expected, but is 0x%02x", level, obj.name, obj.type, *start_ptr); - DBG3(DBG_LIB, "%b", start_ptr, (u_int)(blob->ptr - start_ptr)); + DBG3(DBG_ASN, "%b", start_ptr, (u_int)(blob->ptr - start_ptr)); this->success = FALSE; goto end; } - DBG2(DBG_LIB, "L%d - %s:", level, obj.name); + DBG2(DBG_ASN, "L%d - %s:", level, obj.name); /* In case of "SEQUENCE OF" or "SET OF" start a loop */ if (obj.flags & ASN1_LOOP) @@ -214,11 +214,11 @@ METHOD(asn1_parser_t, iterate, bool, object->len = (size_t)(blob->ptr - start_ptr); if (this->private) { - DBG4(DBG_LIB, "%B", object); + DBG4(DBG_ASN, "%B", object); } else { - DBG3(DBG_LIB, "%B", object); + DBG3(DBG_ASN, "%B", object); } } else if (obj.flags & ASN1_BODY) diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt index 73c068851..5daa6dad6 100644 --- a/src/libstrongswan/asn1/oid.txt +++ b/src/libstrongswan/asn1/oid.txt @@ -97,6 +97,11 @@ 0x0C "sha384WithRSAEncryption" OID_SHA384_WITH_RSA 0x0D "sha512WithRSAEncryption" OID_SHA512_WITH_RSA 0x0E "sha224WithRSAEncryption" OID_SHA224_WITH_RSA + 0x05 "PKCS-5" + 0x03 "pbeWithMD5AndDES-CBC" OID_PBE_MD5_DES_CBC + 0x0A "pbeWithSHA1AndDES-CBC" OID_PBE_SHA1_DES_CBC + 0x0C "id-PBKDF2" OID_PBKDF2 + 0x0D "id-PBES2" OID_PBES2 0x07 "PKCS-7" 0x01 "data" OID_PKCS7_DATA 0x02 "signedData" OID_PKCS7_SIGNED_DATA diff --git a/src/libstrongswan/credentials/auth_cfg.h b/src/libstrongswan/credentials/auth_cfg.h index fbc4b6eda..31c7e7d90 100644 --- a/src/libstrongswan/credentials/auth_cfg.h +++ b/src/libstrongswan/credentials/auth_cfg.h @@ -65,7 +65,6 @@ extern enum_name_t *auth_class_names; * to transport credentials during the authentication process. */ enum auth_rule_t { - /** identity to use for IKEv2 authentication exchange, identification_t* */ AUTH_RULE_IDENTITY, /** authentication class, auth_class_t */ @@ -125,8 +124,8 @@ extern enum_name_t *auth_rule_names; * * RFC4739 defines multiple authentication rounds. This class defines such * a round from a configuration perspective, either for the local or the remote - * peer. Local config are called "rulesets", as they define how we authenticate. - * Remote peer configs are called "constraits", they define what is needed to + * peer. Local configs are called "rulesets". They define how we authenticate. + * Remote peer configs are called "constraits". They define what is needed to * complete the authentication round successfully. * * @verbatim @@ -150,7 +149,7 @@ extern enum_name_t *auth_rule_names; @endverbatim * - * Values for each items are either pointers (casted to void*) or short + * Values for each item are either pointers (casted to void*) or short * integers (use uintptr_t cast). */ struct auth_cfg_t { @@ -164,7 +163,7 @@ struct auth_cfg_t { void (*add)(auth_cfg_t *this, auth_rule_t rule, ...); /** - * Get an rule value. + * Get a rule value. * * @param rule rule type * @return bool if item has been found @@ -179,9 +178,9 @@ struct auth_cfg_t { enumerator_t* (*create_enumerator)(auth_cfg_t *this); /** - * Replace an rule at enumerator position. + * Replace a rule at enumerator position. * - * @param pos enumerator position position + * @param pos enumerator position * @param rule rule type * @param ... associated value to rule */ @@ -192,7 +191,7 @@ struct auth_cfg_t { * Check if a used config fulfills a set of configured constraints. * * @param constraints required authorization rules - * @param log_error wheter to log compliance errors + * @param log_error whether to log compliance errors * @return TRUE if this complies with constraints */ bool (*complies)(auth_cfg_t *this, auth_cfg_t *constraints, bool log_error); @@ -208,20 +207,20 @@ struct auth_cfg_t { /** * Purge all rules in a config. * - * @param keep_ca wheter to keep AUTH_RULE_CA_CERT entries + * @param keep_ca whether to keep AUTH_RULE_CA_CERT entries */ void (*purge)(auth_cfg_t *this, bool keep_ca); /** * Check two configs for equality. * - * @param other other config to compaire against this + * @param other other config to compare against this * @return TRUE if auth infos identical */ bool (*equals)(auth_cfg_t *this, auth_cfg_t *other); /** - * Clone a authentication config, including all rules. + * Clone an authentication config, including all rules. * * @return cloned configuration */ diff --git a/src/libstrongswan/credentials/builder.c b/src/libstrongswan/credentials/builder.c index 68c54fb45..d3157c80e 100644 --- a/src/libstrongswan/credentials/builder.c +++ b/src/libstrongswan/credentials/builder.c @@ -23,6 +23,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END, "BUILD_BLOB_PEM", "BUILD_BLOB_PGP", "BUILD_BLOB_DNSKEY", + "BUILD_BLOB_ALGID_PARAMS", "BUILD_KEY_SIZE", "BUILD_SIGNING_KEY", "BUILD_SIGNING_CERT", diff --git a/src/libstrongswan/credentials/builder.h b/src/libstrongswan/credentials/builder.h index 325b668cd..41250ccae 100644 --- a/src/libstrongswan/credentials/builder.h +++ b/src/libstrongswan/credentials/builder.h @@ -28,8 +28,8 @@ typedef enum builder_part_t builder_part_t; /** * Constructor function to build credentials. * - * Any added parts are cloned/refcounted by the builder implementation, a - * caller may need to free the passed ressources themself. + * Any added parts are cloned/refcounted by the builder implementation. + * Callers may need to free the passed resources themselves. * * @param subtype constructor specific subtype, e.g. a certificate_type_t * @param args list of builder part types, followed by parts, BUILD_END @@ -53,10 +53,12 @@ enum builder_part_t { BUILD_BLOB_ASN1_DER, /** PEM encoded ASN.1/PGP blob, chunk_t */ BUILD_BLOB_PEM, - /** OpenPGP key blob, chunk_t */ + /** OpenPGP key blob, chunk_t */ BUILD_BLOB_PGP, /** DNS public key blob (RFC 4034, RSA specifc RFC 3110), chunk_t */ BUILD_BLOB_DNSKEY, + /** parameters from algorithmIdentifier (ASN.1 blob), chunk_t */ + BUILD_BLOB_ALGID_PARAMS, /** key size in bits, as used for key generation, u_int */ BUILD_KEY_SIZE, /** private key to use for signing, private_key_t* */ diff --git a/src/libstrongswan/credentials/cert_validator.h b/src/libstrongswan/credentials/cert_validator.h index 733d9d612..00e30d7a0 100644 --- a/src/libstrongswan/credentials/cert_validator.h +++ b/src/libstrongswan/credentials/cert_validator.h @@ -39,7 +39,7 @@ struct cert_validator_t { * * @param subject subject certificate to check * @param issuer issuer of subject - * @param online wheter to do online revocation checking + * @param online whether to do online revocation checking * @param pathlen the current length of the path bottom-up * @param anchor is issuer trusted root anchor * @param auth container for resulting authentication info diff --git a/src/libstrongswan/credentials/certificates/x509.h b/src/libstrongswan/credentials/certificates/x509.h index 8bd2a6a83..00171a718 100644 --- a/src/libstrongswan/credentials/certificates/x509.h +++ b/src/libstrongswan/credentials/certificates/x509.h @@ -78,12 +78,12 @@ enum x509_constraint_t { * X.509 certPolicy extension. */ struct x509_cert_policy_t { - /** OID of certPolicy */ - chunk_t oid; /** Certification Practice Statement URI qualifier */ char *cps_uri; /** UserNotice Text qualifier */ char *unotice_text; + /** OID of certPolicy */ + chunk_t oid; }; /** diff --git a/src/libstrongswan/credentials/cred_encoding.c b/src/libstrongswan/credentials/cred_encoding.c index a7637b598..4865984dd 100644 --- a/src/libstrongswan/credentials/cred_encoding.c +++ b/src/libstrongswan/credentials/cred_encoding.c @@ -116,7 +116,7 @@ METHOD(cred_encoding_t, get_cache, bool, { chunk_t *chunk; - if (type >= CRED_ENCODING_MAX || type < 0) + if (type >= CRED_ENCODING_MAX || (int)type < 0) { return FALSE; } @@ -142,7 +142,7 @@ static bool encode(private_cred_encoding_t *this, cred_encoding_type_t type, bool success = FALSE; chunk_t *chunk; - if (type >= CRED_ENCODING_MAX || type < 0) + if (type >= CRED_ENCODING_MAX || (int)type < 0) { return FALSE; } @@ -195,7 +195,7 @@ METHOD(cred_encoding_t, cache, void, { chunk_t *chunk; - if (type >= CRED_ENCODING_MAX || type < 0) + if (type >= CRED_ENCODING_MAX || (int)type < 0) { return free(encoding.ptr); } diff --git a/src/libstrongswan/credentials/cred_encoding.h b/src/libstrongswan/credentials/cred_encoding.h index e2d69691e..b029fe2ac 100644 --- a/src/libstrongswan/credentials/cred_encoding.h +++ b/src/libstrongswan/credentials/cred_encoding.h @@ -59,7 +59,7 @@ bool cred_encoding_args(va_list args, ...); /** * Encoding type of a fingerprint/credential. * - * Fingerprints have have the KEYID_*, public keys the PUBKEY_* and + * Fingerprints have the KEYID_*, public keys the PUBKEY_* and * private keys the PRIVKEY_* prefix. */ enum cred_encoding_type_t { diff --git a/src/libstrongswan/credentials/credential_factory.h b/src/libstrongswan/credentials/credential_factory.h index 709dc916a..c31601245 100644 --- a/src/libstrongswan/credentials/credential_factory.h +++ b/src/libstrongswan/credentials/credential_factory.h @@ -54,7 +54,7 @@ struct credential_factory_t { * The variable argument list takes builder_part_t types followed * by the type specific value. The list must be terminated using BUILD_END. * All passed parts get cloned/refcounted by the builder functions, - * so free up allocated ressources after successful and unsuccessful + * so free up allocated resources after successful and unsuccessful * invocations. * * @param type credential type to build diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c index b8f8ae8e3..d54359ebf 100644 --- a/src/libstrongswan/credentials/credential_manager.c +++ b/src/libstrongswan/credentials/credential_manager.c @@ -921,7 +921,7 @@ METHOD(credential_manager_t, create_public_enumerator, enumerator_t*, } /** - * Check if an helper contains a certificate as trust anchor + * Check if a helper contains a certificate as trust anchor */ static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert) { @@ -1004,7 +1004,7 @@ static auth_cfg_t *build_trustchain(private_credential_manager_t *this, } /** - * find a private key of a give certificate + * find a private key of a given certificate */ static private_key_t *get_private_by_cert(private_credential_manager_t *this, certificate_t *cert, key_type_t type) diff --git a/src/libstrongswan/credentials/credential_manager.h b/src/libstrongswan/credentials/credential_manager.h index 4b9f914c4..ad789c718 100644 --- a/src/libstrongswan/credentials/credential_manager.h +++ b/src/libstrongswan/credentials/credential_manager.h @@ -36,11 +36,11 @@ typedef struct credential_manager_t credential_manager_t; * Manages credentials using credential_sets. * * The credential manager is the entry point of the credential framework. It - * uses so called "sets" to access credentials in a modular fashion, these + * uses so called "sets" to access credentials in a modular fashion. These * are implemented through the credential_set_t interface. * The manager additionally does trust chain verification and trust status - * chaching. A set may call the managers methods if it needs credentials itself, - * the manager uses recursive locking. + * caching. A set may call the managers methods if it needs credentials itself. + * The manager uses recursive locking. * * @verbatim @@ -62,8 +62,8 @@ typedef struct credential_manager_t credential_manager_t; @endverbatim * - * The credential manager uses rwlocks for performance reasons, credential - * sets must be fully thread save. + * The credential manager uses rwlocks for performance reasons. Credential + * sets must be fully thread-safe. */ struct credential_manager_t { @@ -84,7 +84,7 @@ struct credential_manager_t { * * The enumerator enumerates over: * shared_key_t*, id_match_t me, id_match_t other - * But must accepts values for the id_matches. + * But must accept values for the id_matches. * * @param type kind of requested shared key * @param first first subject between key is shared @@ -120,7 +120,7 @@ struct credential_manager_t { * * @param type kind of requested shared key * @param me own identity - * @param other peers identity + * @param other peer identity * @return shared_key_t, NULL if none found */ shared_key_t *(*get_shared)(credential_manager_t *this, shared_key_type_t type, @@ -130,7 +130,7 @@ struct credential_manager_t { * * The get_private() method gets a secret private key identified by either * the keyid itself or an id the key belongs to. - * The auth parameter contains additional information, such as receipients + * The auth parameter contains additional information, such as recipients * trusted CA certs. Auth gets filled with subject and CA certificates * needed to validate a created signature. * @@ -163,7 +163,7 @@ struct credential_manager_t { /** * Create an enumerator over trusted public keys. * - * This method gets a an enumerator over trusted public keys to verify a + * This method creates an enumerator over trusted public keys to verify a * signature created by id. The auth parameter contains additional * authentication infos, e.g. peer and intermediate certificates. * The resulting enumerator enumerates over public_key_t *, auth_cfg_t *, @@ -180,7 +180,7 @@ struct credential_manager_t { key_type_t type, identification_t *id, auth_cfg_t *auth); /** - * Cache a certificate by invoking cache_cert() on all registerd sets. + * Cache a certificate by invoking cache_cert() on all registered sets. * * @param cert certificate to cache */ @@ -199,8 +199,8 @@ struct credential_manager_t { /** * Check if a given subject certificate is issued by an issuer certificate. * - * This operation does signature verification, but uses the credential - * managers cache for to speed up the operation. + * This operation does signature verification using the credential + * manager's cache to speed up the operation. * * @param subject subject certificate to check * @param issuer issuer certificate that potentially has signed subject @@ -228,7 +228,7 @@ struct credential_manager_t { * * To add a credential set for the current trustchain verification * operation, sets may be added for the calling thread only. This - * does not require a write lock and is therefore a much less expensive + * does not require a write lock and is therefore a much cheaper * operation. * The exclusive option allows to disable all other credential sets * until the set is deregistered. diff --git a/src/libstrongswan/credentials/credential_set.h b/src/libstrongswan/credentials/credential_set.h index 0eee237cb..8673c484f 100644 --- a/src/libstrongswan/credentials/credential_set.h +++ b/src/libstrongswan/credentials/credential_set.h @@ -38,7 +38,7 @@ typedef struct credential_set_t credential_set_t; * A credential set enumerator may not block the credential set, i.e. multiple * threads must be able to hold multiple enumerators, as the credential manager * is higly parallelized. The best way to achieve this is by using shared - * read locks for the enumerators only. Otherwiese deadlocks will occur. + * read locks for the enumerators only. Otherwise deadlocks will occur. * The writing cache_cert() routine is called by the manager only if no * enumerator is alive, so it is save to use a write lock there. */ @@ -97,7 +97,7 @@ struct credential_set_t { /** * Cache a certificate in the credential set. * - * The caching policy is implementation dependent, the sets may cache the + * The caching policy is implementation dependent. The sets may cache the * certificate in-memory, persistent on disk or not at all. * * @param cert certificate to cache diff --git a/src/libstrongswan/crypto/crypto_tester.c b/src/libstrongswan/crypto/crypto_tester.c index 4635dccea..8b1daa885 100644 --- a/src/libstrongswan/crypto/crypto_tester.c +++ b/src/libstrongswan/crypto/crypto_tester.c @@ -102,6 +102,8 @@ static const char* get_name(void *sym) return "unknown"; } +#ifdef CLOCK_THREAD_CPUTIME_ID + /** * Start a benchmark timer */ @@ -122,6 +124,14 @@ static u_int end_timing(struct timespec *start) (end.tv_sec - start->tv_sec) * 1000; } +#else /* CLOCK_THREAD_CPUTIME_ID */ + +/* Make benchmarking a no-op if CLOCK_THREAD_CPUTIME_ID is not available */ +#define start_timing(start) ((start)->tv_sec = 0, (start)->tv_nsec = 0) +#define end_timing(...) (this->bench_time) + +#endif /* CLOCK_THREAD_CPUTIME_ID */ + /** * Benchmark a crypter */ diff --git a/src/libstrongswan/crypto/diffie_hellman.c b/src/libstrongswan/crypto/diffie_hellman.c index 5f7365321..1124ee6f7 100644 --- a/src/libstrongswan/crypto/diffie_hellman.c +++ b/src/libstrongswan/crypto/diffie_hellman.c @@ -64,7 +64,8 @@ static struct { 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37, 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, - 0xF4,0x4C,0x42,0xE9,0xA6,0x3A,0x36,0x20,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0xF4,0x4C,0x42,0xE9,0xA6,0x3A,0x36,0x20,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_1024_BIT, .opt_exp = 32, .public = { @@ -77,7 +78,8 @@ static struct { 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6, 0xF4,0x4C,0x42,0xE9,0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,0x7C,0x4B,0x1F,0xE6, - 0x49,0x28,0x66,0x51,0xEC,0xE6,0x53,0x81,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0x49,0x28,0x66,0x51,0xEC,0xE6,0x53,0x81,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_1536_BIT, .opt_exp = 32, .public = { @@ -94,7 +96,8 @@ static struct { 0x98,0xDA,0x48,0x36,0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,0x20,0x85,0x52,0xBB, 0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04, - 0xF1,0x74,0x6C,0x08,0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0xF1,0x74,0x6C,0x08,0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_2048_BIT, .opt_exp = 48, .public = { @@ -115,7 +118,8 @@ static struct { 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2,0xEC,0x07,0xA2,0x8F, 0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9,0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18, 0x39,0x95,0x49,0x7C,0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, - 0x15,0x72,0x8E,0x5A,0x8A,0xAC,0xAA,0x68,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0x15,0x72,0x8E,0x5A,0x8A,0xAC,0xAA,0x68,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_3072_BIT, .opt_exp = 48, .public = { @@ -144,7 +148,8 @@ static struct { 0xD8,0x76,0x02,0x73,0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0,0xBA,0xD9,0x46,0xE2, 0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31,0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E, - 0x4B,0x82,0xD1,0x20,0xA9,0x3A,0xD2,0xCA,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0x4B,0x82,0xD1,0x20,0xA9,0x3A,0xD2,0xCA,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_4096_BIT, .opt_exp = 64, .public = { @@ -181,7 +186,8 @@ static struct { 0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED,0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF, 0xB8,0x1B,0xDD,0x76,0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, 0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC,0x90,0xA6,0xC0,0x8F, - 0x4D,0xF4,0x35,0xC9,0x34,0x06,0x31,0x99,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0x4D,0xF4,0x35,0xC9,0x34,0x06,0x31,0x99,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_6144_BIT, .opt_exp = 64, .public = { @@ -234,7 +240,8 @@ static struct { 0xF5,0x50,0xAA,0x3D,0x8A,0x1F,0xBF,0xF0,0xEB,0x19,0xCC,0xB1,0xA3,0x13,0xD5,0x5C, 0xDA,0x56,0xC9,0xEC,0x2E,0xF2,0x96,0x32,0x38,0x7F,0xE8,0xD7,0x6E,0x3C,0x04,0x68, 0x04,0x3E,0x8F,0x66,0x3F,0x48,0x60,0xEE,0x12,0xBF,0x2D,0x5B,0x0B,0x74,0x74,0xD6, - 0xE6,0x94,0xF9,0x1E,0x6D,0xCC,0x40,0x24,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0xE6,0x94,0xF9,0x1E,0x6D,0xCC,0x40,0x24,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_8192_BIT, .opt_exp = 64, .public = { @@ -303,7 +310,8 @@ static struct { 0x40,0x09,0x43,0x8B,0x48,0x1C,0x6C,0xD7,0x88,0x9A,0x00,0x2E,0xD5,0xEE,0x38,0x2B, 0xC9,0x19,0x0D,0xA6,0xFC,0x02,0x6E,0x47,0x95,0x58,0xE4,0x47,0x56,0x77,0xE9,0xAA, 0x9E,0x30,0x50,0xE2,0x76,0x56,0x94,0xDF,0xC8,0x1F,0x56,0xE8,0x80,0xB9,0x6E,0x71, - 0x60,0xC9,0x80,0xDD,0x98,0xED,0xD3,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF) + 0x60,0xC9,0x80,0xDD,0x98,0xED,0xD3,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF), + .exp_len = 0, }, },{ .group = MODP_1024_160, .opt_exp = 20, .public = { diff --git a/src/libstrongswan/crypto/diffie_hellman.h b/src/libstrongswan/crypto/diffie_hellman.h index aca793b71..cab3b1ba7 100644 --- a/src/libstrongswan/crypto/diffie_hellman.h +++ b/src/libstrongswan/crypto/diffie_hellman.h @@ -74,8 +74,7 @@ struct diffie_hellman_t { /** * Returns the shared secret of this diffie hellman exchange. * - * Space for returned secret is allocated and must be - * freed by the caller. + * Space for returned secret is allocated and must be freed by the caller. * * @param secret shared secret will be written into this chunk * @return SUCCESS, FAILED if not both DH values are set @@ -108,7 +107,7 @@ struct diffie_hellman_t { diffie_hellman_group_t (*get_dh_group) (diffie_hellman_t *this); /** - * Destroys an diffie_hellman_t object. + * Destroys a diffie_hellman_t object. */ void (*destroy) (diffie_hellman_t *this); }; diff --git a/src/libstrongswan/crypto/pkcs7.c b/src/libstrongswan/crypto/pkcs7.c index 2593d8b79..a4d0e71fe 100644 --- a/src/libstrongswan/crypto/pkcs7.c +++ b/src/libstrongswan/crypto/pkcs7.c @@ -825,7 +825,7 @@ METHOD(pkcs7_t, build_signedData, bool, /* take the current time as signingTime */ time_t now = time(NULL); - chunk_t signingTime = asn1_from_time(&now, ASN1_UTCTIME); + chunk_t signingTime = asn1_from_time(&now, ASN1_UTCTIME); chunk_t messageDigest, attributes; diff --git a/src/libstrongswan/crypto/proposal/proposal_keywords.txt b/src/libstrongswan/crypto/proposal/proposal_keywords.txt index 4ef664d8f..b16e2eccb 100644 --- a/src/libstrongswan/crypto/proposal/proposal_keywords.txt +++ b/src/libstrongswan/crypto/proposal/proposal_keywords.txt @@ -118,6 +118,7 @@ twofish192, ENCRYPTION_ALGORITHM, ENCR_TWOFISH_CBC, 192 twofish256, ENCRYPTION_ALGORITHM, ENCR_TWOFISH_CBC, 256 sha, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0 sha1, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0 +sha1_160, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_160, 0 sha256, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0 sha2_256, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_128, 0 sha256_96, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_256_96, 0 @@ -127,6 +128,7 @@ sha2_384, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_384_192, 0 sha512, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0 sha2_512, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA2_512_256, 0 md5, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0 +md5_128, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_128, 0 aesxcbc, INTEGRITY_ALGORITHM, AUTH_AES_XCBC_96, 0 camelliaxcbc, INTEGRITY_ALGORITHM, AUTH_CAMELLIA_XCBC_96, 0 modpnull, DIFFIE_HELLMAN_GROUP, MODP_NULL, 0 diff --git a/src/libstrongswan/debug.c b/src/libstrongswan/debug.c index 608303445..d6c5b06b6 100644 --- a/src/libstrongswan/debug.c +++ b/src/libstrongswan/debug.c @@ -26,6 +26,7 @@ ENUM(debug_names, DBG_DMN, DBG_LIB, "CFG", "KNL", "NET", + "ASN", "ENC", "TNC", "IMC", @@ -44,6 +45,7 @@ ENUM(debug_lower_names, DBG_DMN, DBG_LIB, "cfg", "knl", "net", + "asn", "enc", "tnc", "imc", diff --git a/src/libstrongswan/debug.h b/src/libstrongswan/debug.h index 849d28f9f..2a6ff98ad 100644 --- a/src/libstrongswan/debug.h +++ b/src/libstrongswan/debug.h @@ -48,6 +48,8 @@ enum debug_t { DBG_KNL, /** networking/sockets */ DBG_NET, + /** low-level encoding/decoding (ASN.1, X.509 etc.) */ + DBG_ASN, /** message encoding/decoding */ DBG_ENC, /** trusted network connect */ diff --git a/src/libstrongswan/fetcher/fetcher_manager.h b/src/libstrongswan/fetcher/fetcher_manager.h index 15250d531..449f284f7 100644 --- a/src/libstrongswan/fetcher/fetcher_manager.h +++ b/src/libstrongswan/fetcher/fetcher_manager.h @@ -26,7 +26,7 @@ typedef struct fetcher_manager_t fetcher_manager_t; #include <fetcher/fetcher.h> /** - * Fetches from URIs using registerd fetcher_t instances. + * Fetches from URIs using registered fetcher_t instances. */ struct fetcher_manager_t { diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index 6ed4d1285..cd6a41f44 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -61,6 +61,9 @@ void library_deinit() detailed = lib->settings->get_bool(lib->settings, "libstrongswan.leak_detective.detailed", TRUE); + /* make sure the cache is clear before unloading plugins */ + lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); + this->public.scheduler->destroy(this->public.scheduler); this->public.processor->destroy(this->public.processor); this->public.plugins->destroy(this->public.plugins); diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c index f4c4759bf..950504573 100644 --- a/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_ec_private_key.c @@ -1,6 +1,6 @@ /* + * Copyright (C) 2008-2012 Tobias Brunner * Copyright (C) 2009 Martin Willi - * Copyright (C) 2008 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -371,14 +371,17 @@ openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type, va_list args) { private_openssl_ec_private_key_t *this; - chunk_t blob = chunk_empty; + chunk_t par = chunk_empty, key = chunk_empty; while (TRUE) { switch (va_arg(args, builder_part_t)) { + case BUILD_BLOB_ALGID_PARAMS: + par = va_arg(args, chunk_t); + continue; case BUILD_BLOB_ASN1_DER: - blob = va_arg(args, chunk_t); + key = va_arg(args, chunk_t); continue; case BUILD_END: break; @@ -389,18 +392,36 @@ openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type, } this = create_empty(); - this->ec = d2i_ECPrivateKey(NULL, (const u_char**)&blob.ptr, blob.len); - if (!this->ec) + + if (par.ptr) { - destroy(this); - return NULL; + this->ec = d2i_ECParameters(NULL, (const u_char**)&par.ptr, par.len); + if (!this->ec) + { + goto error; + } + if (!d2i_ECPrivateKey(&this->ec, (const u_char**)&key.ptr, key.len)) + { + goto error; + } + } + else + { + this->ec = d2i_ECPrivateKey(NULL, (const u_char**)&key.ptr, key.len); + if (!this->ec) + { + goto error; + } } if (!EC_KEY_check_key(this->ec)) { - destroy(this); - return NULL; + goto error; } return &this->public; + +error: + destroy(this); + return NULL; } #endif /* OPENSSL_NO_EC */ diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c index 422e31521..a24bae5d6 100644 --- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c +++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c @@ -44,6 +44,8 @@ struct private_openssl_rsa_public_key_t { refcount_t ref; }; + + /** * Verification of an EMPSA PKCS1 signature described in PKCS#1 */ @@ -386,4 +388,3 @@ openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type, destroy(this); return NULL; } - diff --git a/src/libstrongswan/plugins/pem/pem_builder.c b/src/libstrongswan/plugins/pem/pem_builder.c index b760adda9..f05d348ee 100644 --- a/src/libstrongswan/plugins/pem/pem_builder.c +++ b/src/libstrongswan/plugins/pem/pem_builder.c @@ -73,7 +73,7 @@ static bool find_boundary(char* tag, chunk_t *line) { if (present("-----", line)) { - DBG2(DBG_LIB, " -----%s %.*s-----", tag, (int)name.len, name.ptr); + DBG2(DBG_ASN, " -----%s %.*s-----", tag, (int)name.len, name.ptr); return TRUE; } line->ptr++; line->len--; name.len++; @@ -99,7 +99,7 @@ static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5); if (hasher == NULL) { - DBG1(DBG_LIB, " MD5 hash algorithm not available"); + DBG1(DBG_ASN, " MD5 hash algorithm not available"); return NOT_SUPPORTED; } hash.len = hasher->get_hash_size(hasher); @@ -121,7 +121,7 @@ static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size); if (crypter == NULL) { - DBG1(DBG_LIB, " %N encryption algorithm not available", + DBG1(DBG_ASN, " %N encryption algorithm not available", encryption_algorithm_names, alg); return NOT_SUPPORTED; } @@ -131,7 +131,7 @@ static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, blob->len % crypter->get_block_size(crypter)) { crypter->destroy(crypter); - DBG1(DBG_LIB, " data size is not multiple of block size"); + DBG1(DBG_ASN, " data size is not multiple of block size"); return PARSE_ERROR; } crypter->decrypt(crypter, *blob, iv, &decrypted); @@ -155,7 +155,7 @@ static status_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, { if (*last_padding_pos != padding) { - DBG1(DBG_LIB, " invalid passphrase"); + DBG1(DBG_ASN, " invalid passphrase"); return INVALID_ARG; } } @@ -234,7 +234,7 @@ static status_t pem_to_bin(chunk_t *blob, bool *pgp) } /* we are looking for a parameter: value pair */ - DBG2(DBG_LIB, " %.*s", (int)line.len, line.ptr); + DBG2(DBG_ASN, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { @@ -274,7 +274,7 @@ static status_t pem_to_bin(chunk_t *blob, bool *pgp) } else { - DBG1(DBG_LIB, " encryption algorithm '%.*s'" + DBG1(DBG_ASN, " encryption algorithm '%.*s'" " not supported", dek.len, dek.ptr); return NOT_SUPPORTED; } @@ -298,7 +298,7 @@ static status_t pem_to_bin(chunk_t *blob, bool *pgp) *pgp = TRUE; data.ptr++; data.len--; - DBG2(DBG_LIB, " armor checksum: %.*s", (int)data.len, + DBG2(DBG_ASN, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } diff --git a/src/libstrongswan/plugins/pgp/pgp_cert.c b/src/libstrongswan/plugins/pgp/pgp_cert.c index dea183ce2..5b2ec63fc 100644 --- a/src/libstrongswan/plugins/pgp/pgp_cert.c +++ b/src/libstrongswan/plugins/pgp/pgp_cert.c @@ -286,18 +286,18 @@ static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet) } break; default: - DBG1(DBG_LIB, "PGP packet version V%d not supported", + DBG1(DBG_ASN, "PGP packet version V%d not supported", this->version); return FALSE; } if (this->valid) { - DBG2(DBG_LIB, "L2 - created %T, valid %d days", &this->created, FALSE, + DBG2(DBG_ASN, "L2 - created %T, valid %d days", &this->created, FALSE, this->valid); } else { - DBG2(DBG_LIB, "L2 - created %T, never expires", &this->created, FALSE); + DBG2(DBG_ASN, "L2 - created %T, never expires", &this->created, FALSE); } DESTROY_IF(this->key); this->key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY, @@ -318,13 +318,13 @@ static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet) hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); if (hasher == NULL) { - DBG1(DBG_LIB, "no SHA-1 hasher available"); + DBG1(DBG_ASN, "no SHA-1 hasher available"); return FALSE; } hasher->allocate_hash(hasher, pubkey_packet_header, NULL); hasher->allocate_hash(hasher, pubkey_packet, &this->fingerprint); hasher->destroy(hasher); - DBG2(DBG_LIB, "L2 - v4 fingerprint %#B", &this->fingerprint); + DBG2(DBG_ASN, "L2 - v4 fingerprint %#B", &this->fingerprint); } else { @@ -335,7 +335,7 @@ static bool parse_public_key(private_pgp_cert_t *this, chunk_t packet) return FALSE; } this->fingerprint = chunk_clone(this->fingerprint); - DBG2(DBG_LIB, "L2 - v3 fingerprint %#B", &this->fingerprint); + DBG2(DBG_ASN, "L2 - v3 fingerprint %#B", &this->fingerprint); } return TRUE; } @@ -355,7 +355,7 @@ static bool parse_signature(private_pgp_cert_t *this, chunk_t packet) /* we parse only v3 or v4 signature packets */ if (version != 3 && version != 4) { - DBG2(DBG_LIB, "L2 - v%d signature ignored", version); + DBG2(DBG_ASN, "L2 - v%d signature ignored", version); return TRUE; } if (version == 4) @@ -364,7 +364,7 @@ static bool parse_signature(private_pgp_cert_t *this, chunk_t packet) { return FALSE; } - DBG2(DBG_LIB, "L2 - v%d signature of type 0x%02x", version, type); + DBG2(DBG_ASN, "L2 - v%d signature of type 0x%02x", version, type); } else { @@ -377,7 +377,7 @@ static bool parse_signature(private_pgp_cert_t *this, chunk_t packet) { return FALSE; } - DBG2(DBG_LIB, "L2 - v3 signature of type 0x%02x, created %T", type, + DBG2(DBG_ASN, "L2 - v3 signature of type 0x%02x, created %T", type, &created, FALSE); } /* TODO: parse and save signature to a list */ @@ -391,7 +391,7 @@ static bool parse_user_id(private_pgp_cert_t *this, chunk_t packet) { DESTROY_IF(this->user_id); this->user_id = identification_create_from_encoding(ID_KEY_ID, packet); - DBG2(DBG_LIB, "L2 - '%Y'", this->user_id); + DBG2(DBG_ASN, "L2 - '%Y'", this->user_id); return TRUE; } diff --git a/src/libstrongswan/plugins/pgp/pgp_utils.c b/src/libstrongswan/plugins/pgp/pgp_utils.c index 2d85cc0c8..7fd905ce4 100644 --- a/src/libstrongswan/plugins/pgp/pgp_utils.c +++ b/src/libstrongswan/plugins/pgp/pgp_utils.c @@ -79,7 +79,7 @@ bool pgp_read_scalar(chunk_t *blob, size_t bytes, u_int32_t *scalar) if (bytes > blob->len) { - DBG1(DBG_LIB, "PGP data too short to read %d byte scalar", bytes); + DBG1(DBG_ASN, "PGP data too short to read %d byte scalar", bytes); return FALSE; } while (bytes-- > 0) @@ -100,13 +100,13 @@ bool pgp_read_mpi(chunk_t *blob, chunk_t *mpi) if (!pgp_read_scalar(blob, 2, &bits)) { - DBG1(DBG_LIB, "PGP data too short to read MPI length"); + DBG1(DBG_ASN, "PGP data too short to read MPI length"); return FALSE; } bytes = (bits + 7) / 8; if (bytes > blob->len) { - DBG1(DBG_LIB, "PGP data too short to read %d byte MPI", bytes); + DBG1(DBG_ASN, "PGP data too short to read %d byte MPI", bytes); return FALSE; } *mpi = chunk_create(blob->ptr, bytes); @@ -146,7 +146,7 @@ bool pgp_read_packet(chunk_t *blob, chunk_t *data, pgp_packet_tag_t *tag) if (!blob->len) { - DBG1(DBG_LIB, "missing input"); + DBG1(DBG_ASN, "missing input"); return FALSE; } t = blob->ptr[0]; @@ -154,27 +154,27 @@ bool pgp_read_packet(chunk_t *blob, chunk_t *data, pgp_packet_tag_t *tag) /* bit 7 must be set */ if (!(t & 0x80)) { - DBG1(DBG_LIB, "invalid packet tag"); + DBG1(DBG_ASN, "invalid packet tag"); return FALSE; } /* bit 6 set defines new packet format */ if (t & 0x40) { - DBG1(DBG_LIB, "new PGP packet format not supported"); + DBG1(DBG_ASN, "new PGP packet format not supported"); return FALSE; } t = (t & 0x3C) >> 2; if (!pgp_old_packet_length(blob, &len) || len > blob->len) { - DBG1(DBG_LIB, "invalid packet length"); + DBG1(DBG_ASN, "invalid packet length"); return FALSE; } *data = chunk_create(blob->ptr, len); *blob = chunk_skip(*blob, len); *tag = t; - DBG2(DBG_LIB, "L1 - PGP %N (%u bytes)", pgp_packet_tag_names, t, len); - DBG3(DBG_LIB, "%B", data); + DBG2(DBG_ASN, "L1 - PGP %N (%u bytes)", pgp_packet_tag_names, t, len); + DBG3(DBG_ASN, "%B", data); return TRUE; } diff --git a/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c b/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c index a605fabc7..6d022f362 100644 --- a/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c +++ b/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c @@ -81,10 +81,10 @@ static public_key_t *parse_public_key(chunk_t blob) /* skip initial bit string octet defining 0 unused bits */ object = chunk_skip(object, 1); } - DBG2(DBG_LIB, "-- > --"); + DBG2(DBG_ASN, "-- > --"); key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type, BUILD_BLOB_ASN1_DER, object, BUILD_END); - DBG2(DBG_LIB, "-- < --"); + DBG2(DBG_ASN, "-- < --"); break; } } @@ -197,7 +197,7 @@ static private_key_t *parse_rsa_private_key(chunk_t blob) case PRIV_KEY_VERSION: if (object.len > 0 && *object.ptr != 0) { - DBG1(DBG_LIB, "PKCS#1 private key format is not version 1"); + DBG1(DBG_ASN, "PKCS#1 private key format is not version 1"); goto end; } break; diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c index d49a03856..d4ec9235d 100644 --- a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c +++ b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c @@ -726,7 +726,7 @@ pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args) { private_pkcs11_public_key_t *this; chunk_t n, e, blob; - size_t keylen; + size_t keylen = 0; n = e = blob = chunk_empty; while (TRUE) @@ -810,7 +810,7 @@ static private_pkcs11_public_key_t *find_key_by_keyid(pkcs11_library_t *p11, bool found = FALSE; size_t keylen; - switch (type) + switch (key_type) { case KEY_RSA: type = CKK_RSA; diff --git a/src/libstrongswan/plugins/pkcs8/Makefile.am b/src/libstrongswan/plugins/pkcs8/Makefile.am new file mode 100644 index 000000000..bcaf2c6a5 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs8/Makefile.am @@ -0,0 +1,16 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +AM_CFLAGS = -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-pkcs8.la +else +plugin_LTLIBRARIES = libstrongswan-pkcs8.la +endif + +libstrongswan_pkcs8_la_SOURCES = \ + pkcs8_plugin.h pkcs8_plugin.c \ + pkcs8_builder.h pkcs8_builder.c + +libstrongswan_pkcs8_la_LDFLAGS = -module -avoid-version diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c new file mode 100644 index 000000000..346240ae1 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2012 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 "pkcs8_builder.h" + +#include <debug.h> +#include <asn1/oid.h> +#include <asn1/asn1.h> +#include <asn1/asn1_parser.h> +#include <credentials/keys/private_key.h> + +/** + * ASN.1 definition of a privateKeyInfo structure + */ +static const asn1Object_t pkinfoObjects[] = { + { 0, "privateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "privateKeyAlgorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ + { 1, "attributes", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 4 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 5 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PKINFO_PRIVATE_KEY_ALGORITHM 2 +#define PKINFO_PRIVATE_KEY 3 + +/** + * Load a generic private key from an ASN.1 encoded blob + */ +static private_key_t *parse_private_key(chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object, params = chunk_empty; + int objectID; + private_key_t *key = NULL; + key_type_t type = KEY_ANY; + + parser = asn1_parser_create(pkinfoObjects, blob); + parser->set_flags(parser, FALSE, TRUE); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PKINFO_PRIVATE_KEY_ALGORITHM: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + + switch (oid) + { + case OID_RSA_ENCRYPTION: + type = KEY_RSA; + break; + case OID_EC_PUBLICKEY: + type = KEY_ECDSA; + break; + default: + /* key type not supported */ + goto end; + } + break; + } + case PKINFO_PRIVATE_KEY: + { + DBG2(DBG_ASN, "-- > --"); + if (params.ptr) + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + type, BUILD_BLOB_ALGID_PARAMS, + params, BUILD_BLOB_ASN1_DER, + object, BUILD_END); + } + else + { + key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + type, BUILD_BLOB_ASN1_DER, object, + BUILD_END); + } + DBG2(DBG_ASN, "-- < --"); + break; + } + } + } + +end: + parser->destroy(parser); + return key; +} + +/** + * Verify padding of decrypted blob. + * Length of blob is adjusted accordingly. + */ +static bool verify_padding(chunk_t *blob) +{ + u_int8_t padding, count; + + padding = count = blob->ptr[blob->len - 1]; + if (padding > 8) + { + return FALSE; + } + for (; blob->len && count; --blob->len, --count) + { + if (blob->ptr[blob->len - 1] != padding) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Prototype for key derivation functions. + */ +typedef void (*kdf_t)(void *generator, chunk_t password, chunk_t salt, + u_int64_t iterations, chunk_t key); + +/** + * Try to decrypt the given blob with multiple passwords using the given + * key derivation function. keymat is where the kdf function writes the key + * to, key and iv point to the actual keys and initialization vectors resp. + */ +static private_key_t *decrypt_private_key(chunk_t blob, + encryption_algorithm_t encr, size_t key_len, kdf_t kdf, + void *generator, chunk_t salt, u_int64_t iterations, + chunk_t keymat, chunk_t key, chunk_t iv) +{ + enumerator_t *enumerator; + shared_key_t *shared; + crypter_t *crypter; + private_key_t *private_key = NULL; + + crypter = lib->crypto->create_crypter(lib->crypto, encr, key_len); + if (!crypter) + { + DBG1(DBG_ASN, " %N encryption algorithm not available", + encryption_algorithm_names, encr); + return NULL; + } + if (blob.len % crypter->get_block_size(crypter)) + { + DBG1(DBG_ASN, " data size is not a multiple of block size"); + crypter->destroy(crypter); + return NULL; + } + + enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, + SHARED_PRIVATE_KEY_PASS, NULL, NULL); + while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) + { + chunk_t decrypted; + + kdf(generator, shared->get_key(shared), salt, iterations, keymat); + + crypter->set_key(crypter, key); + crypter->decrypt(crypter, blob, iv, &decrypted); + if (verify_padding(&decrypted)) + { + private_key = parse_private_key(decrypted); + if (private_key) + { + chunk_clear(&decrypted); + break; + } + } + chunk_free(&decrypted); + } + enumerator->destroy(enumerator); + crypter->destroy(crypter); + + return private_key; +} + +/** + * Function F of PBKDF2 + */ +static void pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed, + u_int64_t iterations) +{ + chunk_t u; + u_int64_t i; + + u = chunk_alloca(prf->get_block_size(prf)); + prf->get_bytes(prf, seed, u.ptr); + memcpy(block.ptr, u.ptr, block.len); + + for (i = 1; i < iterations; i++) + { + prf->get_bytes(prf, u, u.ptr); + memxor(block.ptr, u.ptr, block.len); + } +} + +/** + * PBKDF2 key derivation function + */ +static void pbkdf2(prf_t *prf, chunk_t password, chunk_t salt, + u_int64_t iterations, chunk_t key) +{ + chunk_t keymat, block, seed; + size_t blocks; + u_int32_t i = 0, *ni; + + prf->set_key(prf, password); + + block.len = prf->get_block_size(prf); + blocks = (key.len - 1) / block.len + 1; + keymat = chunk_alloca(blocks * block.len); + + seed = chunk_cata("cc", salt, chunk_from_thing(i)); + ni = (u_int32_t*)(seed.ptr + salt.len); + + for (; i < blocks; i++) + { + *ni = htonl(i + 1); + block.ptr = keymat.ptr + (i * block.len); + pbkdf2_f(block, prf, seed, iterations); + } + + memcpy(key.ptr, keymat.ptr, key.len); +} + +/** + * Decrypt an encrypted PKCS#8 encoded private key according to PBES2 + */ +static private_key_t *decrypt_private_key_pbes2(chunk_t blob, + encryption_algorithm_t encr, size_t key_len, + chunk_t iv, pseudo_random_function_t prf_func, + chunk_t salt, u_int64_t iterations) +{ + private_key_t *private_key; + prf_t *prf; + chunk_t key; + + prf = lib->crypto->create_prf(lib->crypto, prf_func); + if (!prf) + { + DBG1(DBG_ASN, " %N prf algorithm not available", + pseudo_random_function_names, prf_func); + return NULL; + } + + key = chunk_alloca(key_len); + + private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf2, prf, + salt, iterations, key, key, iv); + + prf->destroy(prf); + return private_key; +} + +/** + * PBKDF1 key derivation function + */ +static void pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt, + u_int64_t iterations, chunk_t key) +{ + chunk_t hash; + u_int64_t i; + + hash = chunk_alloca(hasher->get_hash_size(hasher)); + hasher->get_hash(hasher, password, NULL); + hasher->get_hash(hasher, salt, hash.ptr); + + for (i = 1; i < iterations; i++) + { + hasher->get_hash(hasher, hash, hash.ptr); + } + + memcpy(key.ptr, hash.ptr, key.len); +} + +/** + * Decrypt an encrypted PKCS#8 encoded private key according to PBES1 + */ +static private_key_t *decrypt_private_key_pbes1(chunk_t blob, + encryption_algorithm_t encr, size_t key_len, + hash_algorithm_t hash, chunk_t salt, + u_int64_t iterations) +{ + private_key_t *private_key = NULL; + hasher_t *hasher = NULL; + chunk_t keymat, key, iv; + + hasher = lib->crypto->create_hasher(lib->crypto, hash); + if (!hasher) + { + DBG1(DBG_ASN, " %N hash algorithm not available", + hash_algorithm_names, hash); + goto end; + } + if (hasher->get_hash_size(hasher) < key_len) + { + goto end; + } + + keymat = chunk_alloca(key_len * 2); + key.len = key_len; + key.ptr = keymat.ptr; + iv.len = key_len; + iv.ptr = keymat.ptr + key_len; + + private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf1, + hasher, salt, iterations, keymat, + key, iv); + +end: + DESTROY_IF(hasher); + return private_key; +} + +/** + * Parse an ASN1_INTEGER to a u_int64_t. + */ +static u_int64_t parse_asn1_integer_uint64(chunk_t blob) +{ + u_int64_t val = 0; + int i; + + for (i = 0; i < blob.len; i++) + { /* if it is longer than 8 bytes, we just use the 8 LSBs */ + val <<= 8; + val |= (u_int64_t)blob.ptr[i]; + } + return val; +} + +/** + * ASN.1 definition of a PBKDF2-params structure + * The salt is actually a CHOICE and could be an AlgorithmIdentifier from + * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions). + */ +static const asn1Object_t pbkdf2ParamsObjects[] = { + { 0, "PBKDF2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ + { 1, "iterationCount",ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "keyLength", ASN1_INTEGER, ASN1_OPT|ASN1_BODY }, /* 3 */ + { 1, "end opt", ASN1_EOC, ASN1_END }, /* 4 */ + { 1, "prf", ASN1_EOC, ASN1_DEF|ASN1_RAW }, /* 5 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBKDF2_SALT 1 +#define PBKDF2_ITERATION_COUNT 2 +#define PBKDF2_KEY_LENGTH 3 +#define PBKDF2_PRF 5 + +/** + * Parse a PBKDF2-params structure + */ +static void parse_pbkdf2_params(chunk_t blob, chunk_t *salt, + u_int64_t *iterations, size_t *key_len, + pseudo_random_function_t *prf) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + + parser = asn1_parser_create(pbkdf2ParamsObjects, blob); + + *key_len = 0; /* key_len is optional */ + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBKDF2_SALT: + { + *salt = object; + break; + } + case PBKDF2_ITERATION_COUNT: + { + *iterations = parse_asn1_integer_uint64(object); + break; + } + case PBKDF2_KEY_LENGTH: + { + *key_len = (size_t)parse_asn1_integer_uint64(object); + break; + } + case PBKDF2_PRF: + { /* defaults to id-hmacWithSHA1 */ + *prf = PRF_HMAC_SHA1; + break; + } + } + } + + parser->destroy(parser); +} + +/** + * ASN.1 definition of a PBES2-params structure + */ +static const asn1Object_t pbes2ParamsObjects[] = { + { 0, "PBES2-params", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "keyDerivationFunc", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "encryptionScheme", ASN1_EOC, ASN1_RAW }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBES2PARAMS_KEY_DERIVATION_FUNC 1 +#define PBES2PARAMS_ENCRYPTION_SCHEME 2 + +/** + * Parse a PBES2-params structure + */ +static void parse_pbes2_params(chunk_t blob, chunk_t *salt, + u_int64_t *iterations, size_t *key_len, + pseudo_random_function_t *prf, + encryption_algorithm_t *encr, chunk_t *iv) +{ + asn1_parser_t *parser; + chunk_t object, params; + int objectID; + + parser = asn1_parser_create(pbes2ParamsObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBES2PARAMS_KEY_DERIVATION_FUNC: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + if (oid != OID_PBKDF2) + { /* unsupported key derivation function */ + goto end; + } + parse_pbkdf2_params(params, salt, iterations, key_len, prf); + break; + } + case PBES2PARAMS_ENCRYPTION_SCHEME: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + if (oid != OID_3DES_EDE_CBC) + { /* unsupported encryption scheme */ + goto end; + } + if (*key_len <= 0) + { /* default key len for DES-EDE3-CBC-Pad */ + *key_len = 24; + } + if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING, + parser->get_level(parser) + 1, "IV")) + { + goto end; + } + *encr = ENCR_3DES; + *iv = params; + break; + } + } + } + +end: + parser->destroy(parser); +} + +/** + * ASN.1 definition of a PBEParameter structure + */ +static const asn1Object_t pbeParameterObjects[] = { + { 0, "PBEParameter", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "salt", ASN1_OCTET_STRING, ASN1_BODY }, /* 1 */ + { 1, "iterationCount", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define PBEPARAM_SALT 1 +#define PBEPARAM_ITERATION_COUNT 2 + +/** + * Parse a PBEParameter structure + */ +static void parse_pbe_parameters(chunk_t blob, chunk_t *salt, + u_int64_t *iterations) +{ + asn1_parser_t *parser; + chunk_t object; + int objectID; + + parser = asn1_parser_create(pbeParameterObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case PBEPARAM_SALT: + { + *salt = object; + break; + } + case PBEPARAM_ITERATION_COUNT: + { + *iterations = parse_asn1_integer_uint64(object); + break; + } + } + } + + parser->destroy(parser); +} + +/** + * ASN.1 definition of an encryptedPrivateKeyInfo structure + */ +static const asn1Object_t encryptedPKIObjects[] = { + { 0, "encryptedPrivateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ + { 1, "encryptedData", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ + { 0, "exit", ASN1_EOC, ASN1_EXIT } +}; +#define EPKINFO_ENCRYPTION_ALGORITHM 1 +#define EPKINFO_ENCRYPTED_DATA 2 + +/** + * Load an encrypted private key from an ASN.1 encoded blob + * Schemes per PKCS#5 (RFC 2898) + */ +static private_key_t *parse_encrypted_private_key(chunk_t blob) +{ + asn1_parser_t *parser; + chunk_t object, params, salt, iv; + u_int64_t iterations = 0; + int objectID; + encryption_algorithm_t encr = ENCR_UNDEFINED; + hash_algorithm_t hash = HASH_UNKNOWN; + pseudo_random_function_t prf = PRF_UNDEFINED; + private_key_t *key = NULL; + size_t key_len = 8; + + parser = asn1_parser_create(encryptedPKIObjects, blob); + + while (parser->iterate(parser, &objectID, &object)) + { + switch (objectID) + { + case EPKINFO_ENCRYPTION_ALGORITHM: + { + int oid = asn1_parse_algorithmIdentifier(object, + parser->get_level(parser) + 1, ¶ms); + + switch (oid) + { + case OID_PBE_MD5_DES_CBC: + encr = ENCR_DES; + hash = HASH_MD5; + parse_pbe_parameters(params, &salt, &iterations); + break; + case OID_PBE_SHA1_DES_CBC: + encr = ENCR_DES; + hash = HASH_SHA1; + parse_pbe_parameters(params, &salt, &iterations); + break; + case OID_PBES2: + parse_pbes2_params(params, &salt, &iterations, + &key_len, &prf, &encr, &iv); + break; + default: + /* encryption scheme not supported */ + goto end; + } + break; + } + case EPKINFO_ENCRYPTED_DATA: + { + if (prf != PRF_UNDEFINED) + { + key = decrypt_private_key_pbes2(object, encr, key_len, iv, + prf, salt, iterations); + } + else + { + key = decrypt_private_key_pbes1(object, encr, key_len, hash, + salt, iterations); + } + break; + } + } + } + +end: + parser->destroy(parser); + return key; +} + +/** + * See header. + */ +private_key_t *pkcs8_private_key_load(key_type_t type, va_list args) +{ + chunk_t blob = chunk_empty; + private_key_t *key; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB_ASN1_DER: + blob = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + /* we don't know whether it is encrypted or not, try both ways */ + key = parse_encrypted_private_key(blob); + if (!key) + { + key = parse_private_key(blob); + } + return key; +} + diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.h b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.h new file mode 100644 index 000000000..b07f2d927 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 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. + */ + +/** + * @defgroup pkcs8_builder pkcs8_builder + * @{ @ingroup pkcs8 + */ + +#ifndef PKCS8_BUILDER_H_ +#define PKCS8_BUILDER_H_ + +#include <credentials/builder.h> +#include <credentials/keys/private_key.h> + +/** + * Load an RSA or ECDSA private key from PKCS#8 data. + * + * @param type type of the key, KEY_RSA or KEY_ECDSA + * @param args builder_part_t argument list + * @return private key, NULL on failure + */ +private_key_t *pkcs8_private_key_load(key_type_t type, va_list args); + +#endif /** PKCS8_BUILDER_H_ @}*/ diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c new file mode 100644 index 000000000..f78c83054 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 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 "pkcs8_plugin.h" + +#include <library.h> + +#include "pkcs8_builder.h" + +typedef struct private_pkcs8_plugin_t private_pkcs8_plugin_t; + +/** + * private data of pkcs8_plugin + */ +struct private_pkcs8_plugin_t { + + /** + * public functions + */ + pkcs8_plugin_t public; +}; + +METHOD(plugin_t, get_name, char*, + private_pkcs8_plugin_t *this) +{ + return "pkcs8"; +} + +METHOD(plugin_t, get_features, int, + private_pkcs8_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_REGISTER(PRIVKEY, pkcs8_private_key_load, FALSE), + PLUGIN_PROVIDE(PRIVKEY, KEY_RSA), + PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_pkcs8_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *pkcs8_plugin_create() +{ + private_pkcs8_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} + diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.h b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.h new file mode 100644 index 000000000..03ca950a3 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 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 PURPSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup pkcs8 pkcs8 + * @ingroup plugins + * + * @defgroup pkcs8_plugin pkcs8_plugin + * @{ @ingroup pkcs8 + */ + +#ifndef PKCS8_PLUGIN_H_ +#define PKCS8_PLUGIN_H_ + +#include <plugins/plugin.h> + +typedef struct pkcs8_plugin_t pkcs8_plugin_t; + +/** + * Plugin providing PKCS#8 private key decoding functions + */ +struct pkcs8_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** PKCS8_PLUGIN_H_ @}*/ diff --git a/src/libstrongswan/plugins/plugin_loader.c b/src/libstrongswan/plugins/plugin_loader.c index 4d26a77f8..164b68e60 100644 --- a/src/libstrongswan/plugins/plugin_loader.c +++ b/src/libstrongswan/plugins/plugin_loader.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Tobias Brunner + * Copyright (C) 2010-2012 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -45,6 +45,11 @@ struct private_plugin_loader_t { * List of plugins, as plugin_entry_t */ linked_list_t *plugins; + + /** + * List of names of loaded plugins + */ + char *loaded_plugins; }; /** @@ -204,6 +209,38 @@ METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*, } /** + * Create a list of the names of all loaded plugins + */ +static char* loaded_plugins_list(private_plugin_loader_t *this) +{ + int buf_len = 128, len = 0; + char *buf, *name; + enumerator_t *enumerator; + plugin_t *plugin; + + buf = malloc(buf_len); + buf[0] = '\0'; + enumerator = create_plugin_enumerator(this); + while (enumerator->enumerate(enumerator, &plugin, NULL)) + { + name = plugin->get_name(plugin); + if (len + (strlen(name) + 1) >= buf_len) + { + buf_len <<= 1; + buf = realloc(buf, buf_len); + } + len += snprintf(&buf[len], buf_len - len, "%s ", name); + } + enumerator->destroy(enumerator); + if (len > 0 && buf[len - 1] == ' ') + { + buf[len - 1] = '\0'; + } + return buf; +} + + +/** * Check if a plugin is already loaded */ static bool plugin_loaded(private_plugin_loader_t *this, char *name) @@ -516,6 +553,11 @@ METHOD(plugin_loader_t, load_plugins, bool, /* unload plugins that we were not able to load any features for */ purge_plugins(this); } + if (!critical_failed) + { + free(this->loaded_plugins); + this->loaded_plugins = loaded_plugins_list(this); + } return !critical_failed; } @@ -558,6 +600,8 @@ METHOD(plugin_loader_t, unload, void, } enumerator->destroy(enumerator); } + free(this->loaded_plugins); + this->loaded_plugins = NULL; } /** @@ -606,11 +650,18 @@ METHOD(plugin_loader_t, reload, u_int, return reloaded; } +METHOD(plugin_loader_t, loaded_plugins, char*, + private_plugin_loader_t *this) +{ + return this->loaded_plugins ?: ""; +} + METHOD(plugin_loader_t, destroy, void, private_plugin_loader_t *this) { unload(this); this->plugins->destroy(this->plugins); + free(this->loaded_plugins); free(this); } @@ -627,6 +678,7 @@ plugin_loader_t *plugin_loader_create() .reload = _reload, .unload = _unload, .create_plugin_enumerator = _create_plugin_enumerator, + .loaded_plugins = _loaded_plugins, .destroy = _destroy, }, .plugins = linked_list_create(), diff --git a/src/libstrongswan/plugins/plugin_loader.h b/src/libstrongswan/plugins/plugin_loader.h index b3ad7a35f..7fd07044d 100644 --- a/src/libstrongswan/plugins/plugin_loader.h +++ b/src/libstrongswan/plugins/plugin_loader.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -68,6 +69,15 @@ struct plugin_loader_t { enumerator_t* (*create_plugin_enumerator)(plugin_loader_t *this); /** + * Get a simple list the names of all loaded plugins. + * + * The function returns internal data, do not free. + * + * @return list of the names of all loaded plugins + */ + char* (*loaded_plugins)(plugin_loader_t *this); + + /** * Unload loaded plugins, destroy plugin_loader instance. */ void (*destroy)(plugin_loader_t *this); diff --git a/src/libstrongswan/plugins/x509/x509_ac.c b/src/libstrongswan/plugins/x509/x509_ac.c index 16522bf62..a2cb589e0 100644 --- a/src/libstrongswan/plugins/x509/x509_ac.c +++ b/src/libstrongswan/plugins/x509/x509_ac.c @@ -192,7 +192,7 @@ static bool parse_directoryName(chunk_t blob, int level, bool implicit, identifi } else { - DBG1(DBG_LIB, "more than one directory name - first selected"); + DBG1(DBG_ASN, "more than one directory name - first selected"); directoryName->destroy(directoryName); } } @@ -200,7 +200,7 @@ static bool parse_directoryName(chunk_t blob, int level, bool implicit, identifi } else { - DBG1(DBG_LIB, "no directoryName found"); + DBG1(DBG_ASN, "no directoryName found"); } list->destroy(list); @@ -359,10 +359,10 @@ static bool parse_certificate(private_x509_ac_t *this) break; case AC_OBJ_VERSION: this->version = (object.len) ? (1 + (u_int)*object.ptr) : 1; - DBG2(DBG_LIB, " v%d", this->version); + DBG2(DBG_ASN, " v%d", this->version); if (this->version != 2) { - DBG1(DBG_LIB, "v%d attribute certificates are not " + DBG1(DBG_ASN, "v%d attribute certificates are not " "supported", this->version); goto end; } @@ -408,20 +408,20 @@ static bool parse_certificate(private_x509_ac_t *this) switch (type) { case OID_AUTHENTICATION_INFO: - DBG2(DBG_LIB, " need to parse authenticationInfo"); + DBG2(DBG_ASN, " need to parse authenticationInfo"); break; case OID_ACCESS_IDENTITY: - DBG2(DBG_LIB, " need to parse accessIdentity"); + DBG2(DBG_ASN, " need to parse accessIdentity"); break; case OID_CHARGING_IDENTITY: - DBG2(DBG_LIB, "-- > --"); + DBG2(DBG_ASN, "-- > --"); this->charging = ietf_attributes_create_from_encoding(object); - DBG2(DBG_LIB, "-- < --"); + DBG2(DBG_ASN, "-- < --"); break; case OID_GROUP: - DBG2(DBG_LIB, "-- > --"); + DBG2(DBG_ASN, "-- > --"); this->groups = ietf_attributes_create_from_encoding(object); - DBG2(DBG_LIB, "-- < --"); + DBG2(DBG_ASN, "-- < --"); break; case OID_ROLE: parse_roleSyntax(object, level); @@ -436,21 +436,21 @@ static bool parse_certificate(private_x509_ac_t *this) break; case AC_OBJ_CRITICAL: critical = object.len && *object.ptr; - DBG2(DBG_LIB, " %s",(critical)?"TRUE":"FALSE"); + DBG2(DBG_ASN, " %s",(critical)?"TRUE":"FALSE"); break; case AC_OBJ_EXTN_VALUE: { switch (extn_oid) { case OID_CRL_DISTRIBUTION_POINTS: - DBG2(DBG_LIB, " need to parse crlDistributionPoints"); + DBG2(DBG_ASN, " need to parse crlDistributionPoints"); break; case OID_AUTHORITY_KEY_ID: this->authKeyIdentifier = x509_parse_authorityKeyIdentifier(object, level, &this->authKeySerialNumber); break; case OID_TARGET_INFORMATION: - DBG2(DBG_LIB, " need to parse targetInformation"); + DBG2(DBG_ASN, " need to parse targetInformation"); break; case OID_NO_REV_AVAIL: this->noRevAvail = TRUE; @@ -465,7 +465,7 @@ static bool parse_certificate(private_x509_ac_t *this) NULL); if (this->algorithm != sig_alg) { - DBG1(DBG_LIB, " signature algorithms do not agree"); + DBG1(DBG_ASN, " signature algorithms do not agree"); success = FALSE; goto end; } @@ -528,7 +528,7 @@ static chunk_t build_attr_cert_validity(private_x509_ac_t *this) { return asn1_wrap(ASN1_SEQUENCE, "mm", asn1_from_time(&this->notBefore, ASN1_GENERALIZEDTIME), - asn1_from_time(&this->notAfter, ASN1_GENERALIZEDTIME)); + asn1_from_time(&this->notAfter, ASN1_GENERALIZEDTIME)); } diff --git a/src/libstrongswan/plugins/x509/x509_cert.c b/src/libstrongswan/plugins/x509/x509_cert.c index cba1a4610..59f490025 100644 --- a/src/libstrongswan/plugins/x509/x509_cert.c +++ b/src/libstrongswan/plugins/x509/x509_cert.c @@ -301,7 +301,7 @@ static void parse_basicConstraints(chunk_t blob, int level0, { case BASIC_CONSTRAINTS_CA: isCA = object.len && *object.ptr; - DBG2(DBG_LIB, " %s", isCA ? "TRUE" : "FALSE"); + DBG2(DBG_ASN, " %s", isCA ? "TRUE" : "FALSE"); if (isCA) { this->flags |= X509_CA; @@ -482,7 +482,7 @@ static identification_t *parse_generalName(chunk_t blob, int level0) if (id_type != ID_ANY) { gn = identification_create_from_encoding(id_type, object); - DBG2(DBG_LIB, " '%Y'", gn); + DBG2(DBG_ASN, " '%Y'", gn); goto end; } } @@ -638,7 +638,7 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0, /* parsing went wrong - abort */ goto end; } - DBG2(DBG_LIB, " '%Y'", id); + DBG2(DBG_ASN, " '%Y'", id); if (accessMethod == OID_OCSP && asprintf(&uri, "%Y", id) > 0) { @@ -1137,36 +1137,36 @@ static bool check_address_object(ts_type_t ts_type, chunk_t object) case TS_IPV4_ADDR_RANGE: if (object.len > 5) { - DBG1(DBG_LIB, "IPv4 address object is larger than 5 octets"); + DBG1(DBG_ASN, "IPv4 address object is larger than 5 octets"); return FALSE; } break; case TS_IPV6_ADDR_RANGE: if (object.len > 17) { - DBG1(DBG_LIB, "IPv6 address object is larger than 17 octets"); + DBG1(DBG_ASN, "IPv6 address object is larger than 17 octets"); return FALSE; } break; default: - DBG1(DBG_LIB, "unknown address family"); + DBG1(DBG_ASN, "unknown address family"); return FALSE; } if (object.len == 0) { - DBG1(DBG_LIB, "An ASN.1 bit string must contain at least the " + DBG1(DBG_ASN, "An ASN.1 bit string must contain at least the " "initial octet"); return FALSE; } if (object.len == 1 && object.ptr[0] != 0) { - DBG1(DBG_LIB, "An empty ASN.1 bit string must contain a zero " + DBG1(DBG_ASN, "An empty ASN.1 bit string must contain a zero " "initial octet"); return FALSE; } if (object.ptr[0] > 7) { - DBG1(DBG_LIB, "number of unused bits is too large"); + DBG1(DBG_ASN, "number of unused bits is too large"); return FALSE; } return TRUE; @@ -1204,11 +1204,11 @@ static void parse_ipAddrBlocks(chunk_t blob, int level0, { break; } - DBG2(DBG_LIB, " %N", ts_type_name, ts_type); + DBG2(DBG_ASN, " %N", ts_type_name, ts_type); } break; case IP_ADDR_BLOCKS_INHERIT: - DBG1(DBG_LIB, "inherit choice is not supported"); + DBG1(DBG_ASN, "inherit choice is not supported"); break; case IP_ADDR_BLOCKS_PREFIX: if (!check_address_object(ts_type, object)) @@ -1217,7 +1217,7 @@ static void parse_ipAddrBlocks(chunk_t blob, int level0, } ts = traffic_selector_create_from_rfc3779_format(ts_type, object, object); - DBG2(DBG_LIB, " %R", ts); + DBG2(DBG_ASN, " %R", ts); this->ipAddrBlocks->insert_last(this->ipAddrBlocks, ts); break; case IP_ADDR_BLOCKS_MIN: @@ -1234,7 +1234,7 @@ static void parse_ipAddrBlocks(chunk_t blob, int level0, } ts = traffic_selector_create_from_rfc3779_format(ts_type, min_object, object); - DBG2(DBG_LIB, " %R", ts); + DBG2(DBG_ASN, " %R", ts); this->ipAddrBlocks->insert_last(this->ipAddrBlocks, ts); break; default: @@ -1323,12 +1323,12 @@ static bool parse_certificate(private_x509_cert_t *this) this->version = (object.len) ? (1+(u_int)*object.ptr) : 1; if (this->version < 1 || this->version > 3) { - DBG1(DBG_LIB, "X.509v%d not supported", this->version); + DBG1(DBG_ASN, "X.509v%d not supported", this->version); goto end; } else { - DBG2(DBG_LIB, " X.509v%d", this->version); + DBG2(DBG_ASN, " X.509v%d", this->version); } break; case X509_OBJ_SERIAL_NUMBER: @@ -1339,7 +1339,7 @@ static bool parse_certificate(private_x509_cert_t *this) break; case X509_OBJ_ISSUER: this->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); - DBG2(DBG_LIB, " '%Y'", this->issuer); + DBG2(DBG_ASN, " '%Y'", this->issuer); break; case X509_OBJ_NOT_BEFORE: this->notBefore = asn1_parse_time(object, level); @@ -1349,13 +1349,13 @@ static bool parse_certificate(private_x509_cert_t *this) break; case X509_OBJ_SUBJECT: this->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object); - DBG2(DBG_LIB, " '%Y'", this->subject); + DBG2(DBG_ASN, " '%Y'", this->subject); break; case X509_OBJ_SUBJECT_PUBLIC_KEY_INFO: - DBG2(DBG_LIB, "-- > --"); + DBG2(DBG_ASN, "-- > --"); this->public_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY, BUILD_BLOB_ASN1_DER, object, BUILD_END); - DBG2(DBG_LIB, "-- < --"); + DBG2(DBG_ASN, "-- < --"); if (this->public_key == NULL) { goto end; @@ -1364,7 +1364,7 @@ static bool parse_certificate(private_x509_cert_t *this) case X509_OBJ_OPTIONAL_EXTENSIONS: if (this->version != 3) { - DBG1(DBG_LIB, "Only X.509v3 certificates have extensions"); + DBG1(DBG_ASN, "Only X.509v3 certificates have extensions"); goto end; } break; @@ -1373,7 +1373,7 @@ static bool parse_certificate(private_x509_cert_t *this) break; case X509_OBJ_CRITICAL: critical = object.len && *object.ptr; - DBG2(DBG_LIB, " %s", critical ? "TRUE" : "FALSE"); + DBG2(DBG_ASN, " %s", critical ? "TRUE" : "FALSE"); break; case X509_OBJ_EXTN_VALUE: { @@ -1448,7 +1448,7 @@ static bool parse_certificate(private_x509_cert_t *this) if (critical && lib->settings->get_bool(lib->settings, "libstrongswan.x509.enforce_critical", TRUE)) { - DBG1(DBG_LIB, "critical '%s' extension not supported", + DBG1(DBG_ASN, "critical '%s' extension not supported", (extn_oid == OID_UNKNOWN) ? "unknown" : (char*)oid_names[extn_oid].name); goto end; @@ -1461,7 +1461,7 @@ static bool parse_certificate(private_x509_cert_t *this) this->algorithm = asn1_parse_algorithmIdentifier(object, level, NULL); if (this->algorithm != sig_alg) { - DBG1(DBG_LIB, " signature algorithms do not agree"); + DBG1(DBG_ASN, " signature algorithms do not agree"); goto end; } break; @@ -1491,7 +1491,7 @@ end: hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); if (hasher == NULL) { - DBG1(DBG_LIB, " unable to create hash of certificate, SHA1 not supported"); + DBG1(DBG_ASN, " unable to create hash of certificate, SHA1 not supported"); return NULL; } hasher->allocate_hash(hasher, this->encoding, &this->encoding_hash); @@ -1904,7 +1904,7 @@ chunk_t build_generalName(identification_t *id) context = ASN1_CONTEXT_S_7; break; default: - DBG1(DBG_LIB, "encoding %N as generalName not supported", + DBG1(DBG_ASN, "encoding %N as generalName not supported", id_type_names, id->get_type(id)); return chunk_empty; } diff --git a/src/libstrongswan/plugins/x509/x509_crl.c b/src/libstrongswan/plugins/x509/x509_crl.c index 758505ab5..7bcca16a3 100644 --- a/src/libstrongswan/plugins/x509/x509_crl.c +++ b/src/libstrongswan/plugins/x509/x509_crl.c @@ -242,14 +242,14 @@ static bool parse(private_x509_crl_t *this) break; case CRL_OBJ_VERSION: this->version = (object.len) ? (1+(u_int)*object.ptr) : 1; - DBG2(DBG_LIB, " v%d", this->version); + DBG2(DBG_ASN, " v%d", this->version); break; case CRL_OBJ_SIG_ALG: sig_alg = asn1_parse_algorithmIdentifier(object, level, NULL); break; case CRL_OBJ_ISSUER: this->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object); - DBG2(DBG_LIB, " '%Y'", this->issuer); + DBG2(DBG_ASN, " '%Y'", this->issuer); break; case CRL_OBJ_THIS_UPDATE: this->thisUpdate = asn1_parse_time(object, level); @@ -274,7 +274,7 @@ static bool parse(private_x509_crl_t *this) case CRL_OBJ_CRL_ENTRY_CRITICAL: case CRL_OBJ_CRITICAL: critical = object.len && *object.ptr; - DBG2(DBG_LIB, " %s", critical ? "TRUE" : "FALSE"); + DBG2(DBG_ASN, " %s", critical ? "TRUE" : "FALSE"); break; case CRL_OBJ_CRL_ENTRY_EXTN_VALUE: case CRL_OBJ_EXTN_VALUE: @@ -291,7 +291,7 @@ static bool parse(private_x509_crl_t *this) { revoked->reason = *object.ptr; } - DBG2(DBG_LIB, " '%N'", crl_reason_names, + DBG2(DBG_ASN, " '%N'", crl_reason_names, revoked->reason); } break; @@ -324,7 +324,7 @@ static bool parse(private_x509_crl_t *this) if (critical && lib->settings->get_bool(lib->settings, "libstrongswan.x509.enforce_critical", TRUE)) { - DBG1(DBG_LIB, "critical '%s' extension not supported", + DBG1(DBG_ASN, "critical '%s' extension not supported", (extn_oid == OID_UNKNOWN) ? "unknown" : (char*)oid_names[extn_oid].name); goto end; @@ -338,7 +338,7 @@ static bool parse(private_x509_crl_t *this) this->algorithm = asn1_parse_algorithmIdentifier(object, level, NULL); if (this->algorithm != sig_alg) { - DBG1(DBG_LIB, " signature algorithms do not agree"); + DBG1(DBG_ASN, " signature algorithms do not agree"); goto end; } break; diff --git a/src/libstrongswan/plugins/x509/x509_ocsp_response.c b/src/libstrongswan/plugins/x509/x509_ocsp_response.c index 23b206fb1..7dfef3993 100644 --- a/src/libstrongswan/plugins/x509/x509_ocsp_response.c +++ b/src/libstrongswan/plugins/x509/x509_ocsp_response.c @@ -507,7 +507,7 @@ static bool parse_basicOCSPResponse(private_x509_ocsp_response_t *this, if (version != OCSP_BASIC_RESPONSE_VERSION) { - DBG1(DBG_LIB, " ocsp ResponseData version %d not " + DBG1(DBG_ASN, " ocsp ResponseData version %d not " "supported", version); goto end; } @@ -516,12 +516,12 @@ static bool parse_basicOCSPResponse(private_x509_ocsp_response_t *this, case BASIC_RESPONSE_ID_BY_NAME: this->responderId = identification_create_from_encoding( ID_DER_ASN1_DN, object); - DBG2(DBG_LIB, " '%Y'", this->responderId); + DBG2(DBG_ASN, " '%Y'", this->responderId); break; case BASIC_RESPONSE_ID_BY_KEY: this->responderId = identification_create_from_encoding( ID_KEY_ID, object); - DBG2(DBG_LIB, " '%Y'", this->responderId); + DBG2(DBG_ASN, " '%Y'", this->responderId); break; case BASIC_RESPONSE_PRODUCED_AT: this->producedAt = asn1_to_time(&object, ASN1_GENERALIZEDTIME); @@ -535,7 +535,7 @@ static bool parse_basicOCSPResponse(private_x509_ocsp_response_t *this, break; case BASIC_RESPONSE_CRITICAL: critical = object.len && *object.ptr; - DBG2(DBG_LIB, " %s", critical ? "TRUE" : "FALSE"); + DBG2(DBG_ASN, " %s", critical ? "TRUE" : "FALSE"); break; case BASIC_RESPONSE_EXT_VALUE: if (extn_oid == OID_NONCE) diff --git a/src/libstrongswan/plugins/x509/x509_pkcs10.c b/src/libstrongswan/plugins/x509/x509_pkcs10.c index a19a3998a..ca08db2c6 100644 --- a/src/libstrongswan/plugins/x509/x509_pkcs10.c +++ b/src/libstrongswan/plugins/x509/x509_pkcs10.c @@ -276,7 +276,7 @@ static bool parse_extension_request(private_x509_pkcs10_t *this, chunk_t blob, i break; case PKCS10_EXTN_CRITICAL: critical = object.len && *object.ptr; - DBG2(DBG_LIB, " %s", critical ? "TRUE" : "FALSE"); + DBG2(DBG_ASN, " %s", critical ? "TRUE" : "FALSE"); break; case PKCS10_EXTN_VALUE: { @@ -309,25 +309,25 @@ static bool parse_challengePassword(private_x509_pkcs10_t *this, chunk_t blob, i if (blob.len < 2) { - DBG1(DBG_LIB, "L%d - challengePassword: ASN.1 object smaller " + DBG1(DBG_ASN, "L%d - challengePassword: ASN.1 object smaller " "than 2 octets", level); return FALSE; } tag = *blob.ptr; if (tag < ASN1_UTF8STRING || tag > ASN1_IA5STRING) { - DBG1(DBG_LIB, "L%d - challengePassword: ASN.1 object is not " + DBG1(DBG_ASN, "L%d - challengePassword: ASN.1 object is not " "a character string", level); return FALSE; } if (asn1_length(&blob) == ASN1_INVALID_LENGTH) { - DBG1(DBG_LIB, "L%d - challengePassword: ASN.1 object has an " + DBG1(DBG_ASN, "L%d - challengePassword: ASN.1 object has an " "invalid length", level); return FALSE; } - DBG2(DBG_LIB, "L%d - challengePassword:", level); - DBG4(DBG_LIB, " '%.*s'", blob.len, blob.ptr); + DBG2(DBG_ASN, "L%d - challengePassword:", level); + DBG4(DBG_ASN, " '%.*s'", blob.len, blob.ptr); return TRUE; } @@ -385,14 +385,14 @@ static bool parse_certificate_request(private_x509_pkcs10_t *this) case PKCS10_VERSION: if (object.len > 0 && *object.ptr != 0) { - DBG1(DBG_LIB, "PKCS#10 certificate request format is " + DBG1(DBG_ASN, "PKCS#10 certificate request format is " "not version 1"); goto end; } break; case PKCS10_SUBJECT: this->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object); - DBG2(DBG_LIB, " '%Y'", this->subject); + DBG2(DBG_ASN, " '%Y'", this->subject); break; case PKCS10_SUBJECT_PUBLIC_KEY_INFO: this->public_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, diff --git a/src/libstrongswan/processing/processor.c b/src/libstrongswan/processing/processor.c index be33fcd84..222f1a535 100644 --- a/src/libstrongswan/processing/processor.c +++ b/src/libstrongswan/processing/processor.c @@ -102,7 +102,7 @@ static void restart(private_processor_t *this) { thread_t *thread; - DBG2(DBG_JOB, "terminated worker thread, ID: %u", thread_current_id()); + DBG2(DBG_JOB, "terminated worker thread %.2u", thread_current_id()); /* respawn thread if required */ this->mutex->lock(this->mutex); @@ -152,7 +152,7 @@ static void process_jobs(private_processor_t *this) /* worker threads are not cancellable by default */ thread_cancelability(FALSE); - DBG2(DBG_JOB, "started worker thread, ID: %u", thread_current_id()); + DBG2(DBG_JOB, "started worker thread %.2u", thread_current_id()); this->mutex->lock(this->mutex); while (this->desired_threads >= this->total_threads) @@ -230,7 +230,7 @@ METHOD(processor_t, get_idle_threads, u_int, */ static job_priority_t sane_prio(job_priority_t prio) { - if (prio < 0 || prio >= JOB_PRIO_MAX) + if ((int)prio < 0 || prio >= JOB_PRIO_MAX) { return JOB_PRIO_MAX - 1; } diff --git a/src/libstrongswan/threading/thread.c b/src/libstrongswan/threading/thread.c index 5b6f0d2ce..49a1b8430 100644 --- a/src/libstrongswan/threading/thread.c +++ b/src/libstrongswan/threading/thread.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2009-2012 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +18,19 @@ #include <signal.h> #include <semaphore.h> +#ifdef HAVE_GETTID +#include <sys/types.h> +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_GETTID +#include <sys/syscall.h> +static inline pid_t gettid() +{ + return syscall(SYS_gettid); +} +#endif + #include <library.h> #include <debug.h> @@ -278,6 +291,17 @@ static void *thread_main(private_thread_t *this) sem_wait(&this->created); current_thread->set(current_thread, this); pthread_cleanup_push((thread_cleanup_t)thread_cleanup, this); + + /* TODO: this is not 100% portable as pthread_t is an opaque type (i.e. + * could be of any size, or even a struct) */ +#ifdef HAVE_GETTID + DBG2(DBG_LIB, "created thread %.2d [%u]", + this->id, gettid()); +#else + DBG2(DBG_LIB, "created thread %.2d [%lx]", + this->id, (u_long)this->thread_id); +#endif + res = this->main(this->arg); pthread_cleanup_pop(TRUE); @@ -414,12 +438,20 @@ void thread_exit(void *val) } /** + * A dummy thread value that reserved pthread_key_t value "0". A buggy PKCS#11 + * library mangles this key, without owning it, so we allocate it for them. + */ +static thread_value_t *dummy1; + +/** * Described in header. */ void threads_init() { private_thread_t *main_thread = thread_create_internal(); + dummy1 = thread_value_create(NULL); + main_thread->id = 0; main_thread->thread_id = pthread_self(); current_thread = thread_value_create(NULL); @@ -443,6 +475,8 @@ void threads_deinit() { private_thread_t *main_thread = (private_thread_t*)thread_current(); + dummy1->destroy(dummy1); + main_thread->mutex->lock(main_thread->mutex); thread_destroy(main_thread); current_thread->destroy(current_thread); diff --git a/src/libstrongswan/utils.c b/src/libstrongswan/utils.c index 2fe7f653c..5a104de7f 100644 --- a/src/libstrongswan/utils.c +++ b/src/libstrongswan/utils.c @@ -344,6 +344,28 @@ bool ref_put(refcount_t *ref) pthread_mutex_unlock(&ref_mutex); return !more_refs; } + +/** + * Single mutex for all compare and swap operations. + */ +static pthread_mutex_t cas_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Compare and swap if equal to old value + */ +#define _cas_impl(name, type) \ +bool cas_##name(type *ptr, type oldval, type newval) \ +{ \ + bool swapped; \ + pthread_mutex_lock(&cas_mutex); \ + if ((swapped = (*ptr == oldval))) { *ptr = newval; } \ + pthread_mutex_unlock(&cas_mutex); \ + return swapped; \ +} + +_cas_impl(bool, bool) +_cas_impl(ptr, void*) + #endif /* HAVE_GCC_ATOMIC_OPERATIONS */ /** diff --git a/src/libstrongswan/utils.h b/src/libstrongswan/utils.h index e5e4a10c0..367b3e37c 100644 --- a/src/libstrongswan/utils.h +++ b/src/libstrongswan/utils.h @@ -579,6 +579,11 @@ typedef volatile u_int refcount_t; #define ref_get(ref) {__sync_fetch_and_add(ref, 1); } #define ref_put(ref) (!__sync_sub_and_fetch(ref, 1)) +#define cas_bool(ptr, oldval, newval) \ + (__sync_bool_compare_and_swap(ptr, oldval, newval)) +#define cas_ptr(ptr, oldval, newval) \ + (__sync_bool_compare_and_swap(ptr, oldval, newval)) + #else /* !HAVE_GCC_ATOMIC_OPERATIONS */ /** @@ -601,6 +606,27 @@ void ref_get(refcount_t *ref); */ bool ref_put(refcount_t *ref); +/** + * Atomically replace value of ptr with newval if it currently equals oldval. + * + * @param ptr pointer to variable + * @param oldval old value of the variable + * @param newval new value set if possible + * @return TRUE if value equaled oldval and newval was written + */ +bool cas_bool(bool *ptr, bool oldval, bool newval); + +/** + * Atomically replace value of ptr with newval if it currently equals oldval. + * + * @param ptr pointer to variable + * @param oldval old value of the variable + * @param newval new value set if possible + * @return TRUE if value equaled oldval and newval was written + */ +bool cas_ptr(void **ptr, void *oldval, void *newval); + + #endif /* HAVE_GCC_ATOMIC_OPERATIONS */ /** diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 93cc027de..0a8789335 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -194,6 +194,7 @@ char *whitelist[] = { "__pthread_setspecific", /* glibc functions */ "mktime", + "ctime", "__gmtime_r", "localtime_r", "tzset", diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index 36335e84b..4cc1a1bdb 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -11,6 +11,7 @@ libtls_la_SOURCES = \ tls_prf.h tls_prf.c \ tls_socket.h tls_socket.c \ tls_eap.h tls_eap.c \ + tls_cache.h tls_cache.c \ tls_peer.h tls_peer.c \ tls_server.h tls_server.c \ tls_handshake.h tls_application.h tls.h tls.c diff --git a/src/libtls/tls.c b/src/libtls/tls.c index 3941bea26..2bcaffbc8 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -437,7 +437,7 @@ METHOD(tls_t, destroy, void, */ tls_t *tls_create(bool is_server, identification_t *server, identification_t *peer, tls_purpose_t purpose, - tls_application_t *application) + tls_application_t *application, tls_cache_t *cache) { private_tls_t *this; @@ -472,7 +472,7 @@ tls_t *tls_create(bool is_server, identification_t *server, .purpose = purpose, ); - this->crypto = tls_crypto_create(&this->public); + this->crypto = tls_crypto_create(&this->public, cache); this->alert = tls_alert_create(); if (is_server) { diff --git a/src/libtls/tls.h b/src/libtls/tls.h index 068ba542c..e22b0facc 100644 --- a/src/libtls/tls.h +++ b/src/libtls/tls.h @@ -35,6 +35,7 @@ typedef struct tls_t tls_t; #include <library.h> #include "tls_application.h" +#include "tls_cache.h" /** * TLS/SSL version numbers @@ -240,10 +241,11 @@ void libtls_init(void); * @param peer peer identity, NULL for no client authentication * @param purpose purpose this TLS stack instance is used for * @param application higher layer application or NULL if none + * @param cache session cache to use, or NULL * @return TLS stack */ tls_t *tls_create(bool is_server, identification_t *server, identification_t *peer, tls_purpose_t purpose, - tls_application_t *application); + tls_application_t *application, tls_cache_t *cache); #endif /** TLS_H_ @}*/ diff --git a/src/libtls/tls_cache.c b/src/libtls/tls_cache.c new file mode 100644 index 000000000..a89201ad7 --- /dev/null +++ b/src/libtls/tls_cache.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2011 Martin Willi + * Copyright (C) 2011 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 "tls_cache.h" + +#include <debug.h> +#include <utils/linked_list.h> +#include <utils/hashtable.h> +#include <threading/rwlock.h> + +typedef struct private_tls_cache_t private_tls_cache_t; + +/** + * Private data of an tls_cache_t object. + */ +struct private_tls_cache_t { + + /** + * Public tls_cache_t interface. + */ + tls_cache_t public; + + /** + * Mapping session => entry_t, fast lookup by session + */ + hashtable_t *table; + + /** + * List containing all entries + */ + linked_list_t *list; + + /** + * Lock to list and table + */ + rwlock_t *lock; + + /** + * Session limit + */ + u_int max_sessions; + + /** + * maximum age of a session, in seconds + */ + u_int max_age; +}; + +/** + * Hashtable entry + */ +typedef struct { + /** session identifier */ + chunk_t session; + /** master secret */ + chunk_t master; + /** TLS cipher suite */ + tls_cipher_suite_t suite; + /** optional identity this entry is bound to */ + identification_t *id; + /** time of add */ + time_t t; +} entry_t; + +/** + * Destroy an entry + */ +static void entry_destroy(entry_t *entry) +{ + chunk_clear(&entry->session); + chunk_clear(&entry->master); + DESTROY_IF(entry->id); + free(entry); +} + +/** + * Hashtable hash function + */ +static u_int hash(chunk_t *key) +{ + return chunk_hash(*key); +} + +/** + * Hashtable equals function + */ +static bool equals(chunk_t *a, chunk_t *b) +{ + return chunk_equals(*a, *b); +} + +METHOD(tls_cache_t, create_, void, + private_tls_cache_t *this, chunk_t session, identification_t *id, + chunk_t master, tls_cipher_suite_t suite) +{ + entry_t *entry; + + INIT(entry, + .session = chunk_clone(session), + .master = chunk_clone(master), + .suite = suite, + .id = id ? id->clone(id) : NULL, + .t = time_monotonic(NULL), + ); + + this->lock->write_lock(this->lock); + this->list->insert_first(this->list, entry); + this->table->put(this->table, &entry->session, entry); + if (this->list->get_count(this->list) > this->max_sessions && + this->list->remove_last(this->list, (void**)&entry) == SUCCESS) + { + DBG2(DBG_TLS, "session limit of %u reached, deleting %#B", + this->max_sessions, &entry->session); + this->table->remove(this->table, &entry->session); + entry_destroy(entry); + } + this->lock->unlock(this->lock); + + DBG2(DBG_TLS, "created TLS session %#B, %d sessions", + &session, this->list->get_count(this->list)); +} + +METHOD(tls_cache_t, lookup, tls_cipher_suite_t, + private_tls_cache_t *this, chunk_t session, identification_t *id, + chunk_t* master) +{ + tls_cipher_suite_t suite = 0; + entry_t *entry; + time_t now; + u_int age; + + now = time_monotonic(NULL); + + this->lock->write_lock(this->lock); + entry = this->table->get(this->table, &session); + if (entry) + { + age = now - entry->t; + if (age <= this->max_age) + { + if (!id || !entry->id || id->equals(id, entry->id)) + { + *master = chunk_clone(entry->master); + suite = entry->suite; + } + } + else + { + DBG2(DBG_TLS, "TLS session %#B expired: %u seconds", &session, age); + } + } + this->lock->unlock(this->lock); + + if (suite) + { + DBG2(DBG_TLS, "resuming TLS session %#B, age %u seconds", &session, age); + } + return suite; +} + +METHOD(tls_cache_t, check, chunk_t, + private_tls_cache_t *this, identification_t *id) +{ + chunk_t session = chunk_empty; + enumerator_t *enumerator; + entry_t *entry; + time_t now; + + now = time_monotonic(NULL); + this->lock->read_lock(this->lock); + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->t + this->max_age >= now && + entry->id && id->equals(id, entry->id)) + { + session = chunk_clone(entry->session); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return session; +} + +METHOD(tls_cache_t, destroy, void, + private_tls_cache_t *this) +{ + entry_t *entry; + + while (this->list->remove_last(this->list, (void**)&entry) == SUCCESS) + { + entry_destroy(entry); + } + this->list->destroy(this->list); + this->table->destroy(this->table); + this->lock->destroy(this->lock); + free(this); +} + +/** + * See header + */ +tls_cache_t *tls_cache_create(u_int max_sessions, u_int max_age) +{ + private_tls_cache_t *this; + + INIT(this, + .public = { + .create = _create_, + .lookup = _lookup, + .check = _check, + .destroy = _destroy, + }, + .table = hashtable_create((hashtable_hash_t)hash, + (hashtable_equals_t)equals, 8), + .list = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .max_sessions = max_sessions, + .max_age = max_age, + ); + + return &this->public; +} diff --git a/src/libtls/tls_cache.h b/src/libtls/tls_cache.h new file mode 100644 index 000000000..ea4e2013e --- /dev/null +++ b/src/libtls/tls_cache.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 Martin Willi + * Copyright (C) 2011 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 tls_cache tls_cache + * @{ @ingroup libtls + */ + +#ifndef TLS_CACHE_H_ +#define TLS_CACHE_H_ + +typedef struct tls_cache_t tls_cache_t; + +#include "tls_crypto.h" + +/** + * TLS session cache facility. + */ +struct tls_cache_t { + + /** + * Create a new TLS session entry. + * + * @param session session identifier + * @param id identity the session is bound to + * @param master TLS master secret + * @param suite TLS cipher suite of the session + */ + void (*create)(tls_cache_t *this, chunk_t session, identification_t *id, + chunk_t master, tls_cipher_suite_t suite); + + /** + * Look up a TLS session entry. + * + * @param session session ID to find + * @param id identity the session is bound to + * @param master gets allocated master secret, if session found + * @return TLS suite of session, 0 if none found + */ + tls_cipher_suite_t (*lookup)(tls_cache_t *this, chunk_t session, + identification_t *id, chunk_t* master); + + /** + * Check if we have a session for a given identity. + * + * @param id identity to check + * @return allocated session ID, or chunk_empty + */ + chunk_t (*check)(tls_cache_t *this, identification_t *id); + + /** + * Destroy a tls_cache_t. + */ + void (*destroy)(tls_cache_t *this); +}; + +/** + * Create a tls_cache instance. + * + * @param max_sessions maximum number of sessions to store + * @param max_age maximum age of a session, in seconds + * @return tls cache + */ +tls_cache_t *tls_cache_create(u_int max_sessions, u_int max_age); + +#endif /** TLS_CACHE_H_ @}*/ diff --git a/src/libtls/tls_compression.h b/src/libtls/tls_compression.h index b4832ab06..b2c60d5d6 100644 --- a/src/libtls/tls_compression.h +++ b/src/libtls/tls_compression.h @@ -23,12 +23,12 @@ #include <library.h> +typedef struct tls_compression_t tls_compression_t; + #include "tls.h" #include "tls_alert.h" #include "tls_fragmentation.h" -typedef struct tls_compression_t tls_compression_t; - /** * TLS record protocol compression layer. */ diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c index b9a5d6627..4d84876d0 100644 --- a/src/libtls/tls_crypto.c +++ b/src/libtls/tls_crypto.c @@ -370,6 +370,11 @@ struct private_tls_crypto_t { tls_t *tls; /** + * TLS session cache + */ + tls_cache_t *cache; + + /** * All handshake data concatentated */ chunk_t handshake; @@ -437,7 +442,7 @@ typedef struct { static suite_algs_t suite_algs[] = { { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, KEY_ECDSA, ECP_256_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 }, { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, @@ -447,7 +452,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, KEY_ECDSA, ECP_384_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 }, { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, @@ -457,7 +462,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, ECP_256_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 }, { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, @@ -467,7 +472,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, ECP_384_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 }, { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, @@ -477,7 +482,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_2048_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256,PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 }, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, @@ -487,7 +492,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 }, { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, @@ -497,7 +502,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, KEY_RSA, MODP_2048_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 }, { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, @@ -507,7 +512,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_3072_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 }, { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, @@ -517,12 +522,12 @@ static suite_algs_t suite_algs[] = { }, { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, MODP_2048_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0 }, { TLS_RSA_WITH_AES_128_CBC_SHA, KEY_RSA, MODP_NONE, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 }, { TLS_RSA_WITH_AES_128_CBC_SHA256, @@ -532,7 +537,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_RSA_WITH_AES_256_CBC_SHA, KEY_RSA, MODP_NONE, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 }, { TLS_RSA_WITH_AES_256_CBC_SHA256, @@ -542,7 +547,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, KEY_RSA, MODP_NONE, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 }, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, @@ -552,7 +557,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, KEY_RSA, MODP_NONE, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 }, { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, @@ -562,32 +567,32 @@ static suite_algs_t suite_algs[] = { }, { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, KEY_ECDSA, ECP_256_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0 }, { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, ECP_256_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0 }, { TLS_RSA_WITH_3DES_EDE_CBC_SHA, KEY_RSA, MODP_NONE, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_3DES, 0 }, { TLS_ECDHE_ECDSA_WITH_NULL_SHA, KEY_ECDSA, ECP_256_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_NULL, 0 }, { TLS_ECDHE_RSA_WITH_NULL_SHA, KEY_ECDSA, ECP_256_BIT, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_NULL, 0 }, { TLS_RSA_WITH_NULL_SHA, KEY_RSA, MODP_NONE, - HASH_SHA1, PRF_HMAC_SHA1, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_SHA1_160, ENCR_NULL, 0 }, { TLS_RSA_WITH_NULL_SHA256, @@ -597,7 +602,7 @@ static suite_algs_t suite_algs[] = { }, { TLS_RSA_WITH_NULL_MD5, KEY_RSA, MODP_NONE, - HASH_MD5, PRF_HMAC_MD5, + HASH_SHA256, PRF_HMAC_SHA2_256, AUTH_HMAC_MD5_128, ENCR_NULL, 0 }, }; @@ -834,25 +839,25 @@ static void filter_mac_config_suites(private_tls_crypto_t *this, while (enumerator->enumerate(enumerator, &token)) { if (strcaseeq(token, "md5") && - suites[i].hash == HASH_MD5) + suites[i].mac == AUTH_HMAC_MD5_128) { suites[remaining++] = suites[i]; break; } if (strcaseeq(token, "sha1") && - suites[i].hash == HASH_SHA1) + suites[i].mac == AUTH_HMAC_SHA1_160) { suites[remaining++] = suites[i]; break; } if (strcaseeq(token, "sha256") && - suites[i].hash == HASH_SHA256) + suites[i].mac == AUTH_HMAC_SHA2_256_256) { suites[remaining++] = suites[i]; break; } if (strcaseeq(token, "sha384") && - suites[i].hash == HASH_SHA384) + suites[i].mac == AUTH_HMAC_SHA2_384_384) { suites[remaining++] = suites[i]; break; @@ -1462,13 +1467,15 @@ METHOD(tls_crypto_t, calculate_finished, bool, return TRUE; } -METHOD(tls_crypto_t, derive_secrets, void, - private_tls_crypto_t *this, chunk_t premaster, - chunk_t client_random, chunk_t server_random) +/** + * Derive master secret from premaster, optionally save session + */ +static void derive_master(private_tls_crypto_t *this, chunk_t premaster, + chunk_t session, identification_t *id, + chunk_t client_random, chunk_t server_random) { char master[48]; - chunk_t seed, block, client_write, server_write; - int mks, eks = 0, ivs = 0; + chunk_t seed; /* derive master secret */ seed = chunk_cata("cc", client_random, server_random); @@ -1477,7 +1484,22 @@ METHOD(tls_crypto_t, derive_secrets, void, sizeof(master), master); this->prf->set_key(this->prf, chunk_from_thing(master)); - memset(master, 0, sizeof(master)); + if (this->cache && session.len) + { + this->cache->create(this->cache, session, id, chunk_from_thing(master), + this->suite); + } + memwipe(master, sizeof(master)); +} + +/** + * Expand key material from master secret + */ +static void expand_keys(private_tls_crypto_t *this, + chunk_t client_random, chunk_t server_random) +{ + chunk_t seed, block, client_write, server_write; + int mks, eks = 0, ivs = 0; /* derive key block for key expansion */ mks = this->signer_out->get_key_size(this->signer_out); @@ -1546,6 +1568,57 @@ METHOD(tls_crypto_t, derive_secrets, void, } } } + + /* EAP-MSK */ + if (this->msk_label) + { + seed = chunk_cata("cc", client_random, server_random); + this->msk = chunk_alloc(64); + this->prf->get_bytes(this->prf, this->msk_label, seed, + this->msk.len, this->msk.ptr); + } +} + +METHOD(tls_crypto_t, derive_secrets, void, + private_tls_crypto_t *this, chunk_t premaster, chunk_t session, + identification_t *id, chunk_t client_random, chunk_t server_random) +{ + derive_master(this, premaster, session, id, client_random, server_random); + expand_keys(this, client_random, server_random); +} + +METHOD(tls_crypto_t, resume_session, tls_cipher_suite_t, + private_tls_crypto_t *this, chunk_t session, identification_t *id, + chunk_t client_random, chunk_t server_random) +{ + chunk_t master; + + if (this->cache && session.len) + { + this->suite = this->cache->lookup(this->cache, session, id, &master); + if (this->suite) + { + this->suite = select_cipher_suite(this, &this->suite, 1, KEY_ANY); + if (this->suite) + { + this->prf->set_key(this->prf, master); + expand_keys(this, client_random, server_random); + } + chunk_clear(&master); + } + return this->suite; + } + return 0; +} + +METHOD(tls_crypto_t, get_session, chunk_t, + private_tls_crypto_t *this, identification_t *server) +{ + if (this->cache) + { + return this->cache->check(this->cache, server); + } + return chunk_empty; } METHOD(tls_crypto_t, change_cipher, void, @@ -1566,21 +1639,6 @@ METHOD(tls_crypto_t, change_cipher, void, } } -METHOD(tls_crypto_t, derive_eap_msk, void, - private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random) -{ - if (this->msk_label) - { - chunk_t seed; - - seed = chunk_cata("cc", client_random, server_random); - free(this->msk.ptr); - this->msk = chunk_alloc(64); - this->prf->get_bytes(this->prf, this->msk_label, seed, - this->msk.len, this->msk.ptr); - } -} - METHOD(tls_crypto_t, get_eap_msk, chunk_t, private_tls_crypto_t *this) { @@ -1606,7 +1664,7 @@ METHOD(tls_crypto_t, destroy, void, /** * See header */ -tls_crypto_t *tls_crypto_create(tls_t *tls) +tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache) { private_tls_crypto_t *this; enumerator_t *enumerator; @@ -1628,12 +1686,14 @@ tls_crypto_t *tls_crypto_create(tls_t *tls) .verify_handshake = _verify_handshake, .calculate_finished = _calculate_finished, .derive_secrets = _derive_secrets, + .resume_session = _resume_session, + .get_session = _get_session, .change_cipher = _change_cipher, - .derive_eap_msk = _derive_eap_msk, .get_eap_msk = _get_eap_msk, .destroy = _destroy, }, .tls = tls, + .cache = cache, ); enumerator = lib->creds->create_builder_enumerator(lib->creds); diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h index 35c9b6e05..7430aea66 100644 --- a/src/libtls/tls_crypto.h +++ b/src/libtls/tls_crypto.h @@ -54,13 +54,13 @@ enum tls_cipher_suite_t { TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008, TLS_RSA_WITH_DES_CBC_SHA = 0x0009, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, - TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B, TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C, TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D, TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E, - TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F, + TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F, TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010, - TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011, TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012, TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014, @@ -110,7 +110,7 @@ enum tls_cipher_suite_t { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041, TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042, TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043, - TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044, TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045, TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046, @@ -126,8 +126,8 @@ enum tls_cipher_suite_t { TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085, TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086, TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087, - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088, - TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089, TLS_PSK_WITH_RC4_128_SHA = 0x008A, TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, @@ -511,27 +511,43 @@ struct tls_crypto_t { * Derive the master secret, MAC and encryption keys. * * @param premaster premaster secret + * @param session session identifier to cache master secret + * @param id identity the session is bound to * @param client_random random data from client hello * @param server_random random data from server hello */ void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster, + chunk_t session, identification_t *id, chunk_t client_random, chunk_t server_random); /** - * Change the cipher used at protection layer. + * Try to resume a TLS session, derive key material. * - * @param inbound TRUE to change inbound cipher, FALSE for outbound + * @param session session identifier + * @param id identity the session is bound to + * @param client_random random data from client hello + * @param server_random random data from server hello + * @return selected suite */ - void (*change_cipher)(tls_crypto_t *this, bool inbound); + tls_cipher_suite_t (*resume_session)(tls_crypto_t *this, chunk_t session, + identification_t *id, + chunk_t client_random, + chunk_t server_random); /** - * Derive the EAP-TLS MSK. + * Check if we have a session to resume as a client. * - * @param client_random random data from client hello - * @param server_random random data from server hello + * @param id server identity to get a session for + * @return allocated session identifier, or chunk_empty */ - void (*derive_eap_msk)(tls_crypto_t *this, - chunk_t client_random, chunk_t server_random); + chunk_t (*get_session)(tls_crypto_t *this, identification_t *id); + + /** + * Change the cipher used at protection layer. + * + * @param inbound TRUE to change inbound cipher, FALSE for outbound + */ + void (*change_cipher)(tls_crypto_t *this, bool inbound); /** * Get the MSK to use in EAP-TLS. @@ -548,7 +564,11 @@ struct tls_crypto_t { /** * Create a tls_crypto instance. + * + * @param tls TLS stack + * @param cache TLS session cache + * @return TLS crypto helper */ -tls_crypto_t *tls_crypto_create(tls_t *tls); +tls_crypto_t *tls_crypto_create(tls_t *tls, tls_cache_t *cache); #endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c index c42c16fb8..62e36aaec 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -251,8 +251,9 @@ METHOD(tls_fragmentation_t, process, status_t, switch (type) { case TLS_CHANGE_CIPHER_SPEC: - if (this->handshake->change_cipherspec(this->handshake)) + if (this->handshake->cipherspec_changed(this->handshake, TRUE)) { + this->handshake->change_cipherspec(this->handshake, TRUE); status = NEED_MORE; break; } @@ -324,8 +325,12 @@ static status_t build_handshake(private_tls_fragmentation_t *this) msg->write_data24(msg, hs->get_buf(hs)); DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", tls_handshake_type_names, type, hs->get_buf(hs).len); - hs->destroy(hs); - continue; + if (!this->handshake->cipherspec_changed(this->handshake, FALSE)) + { + hs->destroy(hs); + continue; + } + /* FALL */ case INVALID_STATE: this->output_type = TLS_HANDSHAKE; this->output = chunk_clone(msg->get_buf(msg)); @@ -397,8 +402,9 @@ METHOD(tls_fragmentation_t, build, status_t, } if (!this->output.len) { - if (this->handshake->cipherspec_changed(this->handshake)) + if (this->handshake->cipherspec_changed(this->handshake, FALSE)) { + this->handshake->change_cipherspec(this->handshake, FALSE); *type = TLS_CHANGE_CIPHER_SPEC; *data = chunk_clone(chunk_from_chars(0x01)); return NEED_MORE; diff --git a/src/libtls/tls_fragmentation.h b/src/libtls/tls_fragmentation.h index d80278916..f650e7be8 100644 --- a/src/libtls/tls_fragmentation.h +++ b/src/libtls/tls_fragmentation.h @@ -23,12 +23,12 @@ #include <library.h> +typedef struct tls_fragmentation_t tls_fragmentation_t; + #include "tls.h" #include "tls_alert.h" #include "tls_handshake.h" -typedef struct tls_fragmentation_t tls_fragmentation_t; - /** * TLS record protocol fragmentation layer. */ diff --git a/src/libtls/tls_handshake.h b/src/libtls/tls_handshake.h index 4f6af2a54..bea0024eb 100644 --- a/src/libtls/tls_handshake.h +++ b/src/libtls/tls_handshake.h @@ -62,18 +62,19 @@ struct tls_handshake_t { tls_handshake_type_t *type, bio_writer_t *writer); /** - * Check if the cipher spec for outgoing messages has changed. + * Check if the cipher spec should be changed for outgoing messages. * - * @return TRUE if cipher spec changed + * @param inbound TRUE to check for inbound cipherspec change + * @return TRUE if cipher spec should be changed */ - bool (*cipherspec_changed)(tls_handshake_t *this); + bool (*cipherspec_changed)(tls_handshake_t *this, bool inbound); /** - * Change the cipher spec for incoming messages. + * Change the cipher for a direction. * - * @return TRUE if cipher spec changed + * @param inbound TRUE to change inbound cipherspec, FALSE for outbound */ - bool (*change_cipherspec)(tls_handshake_t *this); + void (*change_cipherspec)(tls_handshake_t *this, bool inbound); /** * Check if the finished message was decoded successfully. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index d3b5ff0aa..6091702cf 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -36,7 +36,7 @@ typedef enum { STATE_CIPHERSPEC_CHANGED_OUT, STATE_FINISHED_SENT, STATE_CIPHERSPEC_CHANGED_IN, - STATE_COMPLETE, + STATE_FINISHED_RECEIVED, } peer_state_t; /** @@ -110,6 +110,16 @@ struct private_tls_peer_t { diffie_hellman_t *dh; /** + * Resuming a session? + */ + bool resume; + + /** + * TLS session identifier + */ + chunk_t session; + + /** * List of server-supported hashsig algorithms */ chunk_t hashsig; @@ -129,7 +139,7 @@ static status_t process_server_hello(private_tls_peer_t *this, u_int8_t compression; u_int16_t version, cipher; chunk_t random, session, ext = chunk_empty; - tls_cipher_suite_t suite; + tls_cipher_suite_t suite = 0; this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, reader->peek(reader)); @@ -155,16 +165,34 @@ static status_t process_server_hello(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); return NEED_MORE; } - suite = cipher; - if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) + + if (chunk_equals(this->session, session)) { - DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", - tls_cipher_suite_names, suite); - this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); - return NEED_MORE; + suite = this->crypto->resume_session(this->crypto, session, this->server, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + if (suite) + { + DBG1(DBG_TLS, "resumed %N using suite %N", + tls_version_names, version, tls_cipher_suite_names, suite); + this->resume = TRUE; + } + } + if (!suite) + { + suite = cipher; + if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) + { + DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", + tls_cipher_suite_names, suite); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + DBG1(DBG_TLS, "negotiated %N using suite %N", + tls_version_names, version, tls_cipher_suite_names, suite); + free(this->session.ptr); + this->session = chunk_clone(session); } - DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", - tls_version_names, version, tls_cipher_suite_names, suite); this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -599,10 +627,9 @@ static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); return NEED_MORE; } - this->state = STATE_COMPLETE; - this->crypto->derive_eap_msk(this->crypto, - chunk_from_thing(this->client_random), - chunk_from_thing(this->server_random)); + this->state = STATE_FINISHED_RECEIVED; + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + return NEED_MORE; } @@ -696,8 +723,9 @@ static status_t send_client_hello(private_tls_peer_t *this, writer->write_uint16(writer, version); writer->write_data(writer, chunk_from_thing(this->client_random)); - /* session identifier => none */ - writer->write_data8(writer, chunk_empty); + /* session identifier */ + this->session = this->crypto->get_session(this->crypto, this->server); + writer->write_data8(writer, this->session); /* add TLS cipher suites */ count = this->crypto->get_cipher_suites(this->crypto, &suites); @@ -886,6 +914,7 @@ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, htoun16(premaster, TLS_1_2); this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), + this->session, this->server, chunk_from_thing(this->client_random), chunk_from_thing(this->server_random)); @@ -930,6 +959,7 @@ static status_t send_key_exchange_dhe(private_tls_peer_t *this, return NEED_MORE; } this->crypto->derive_secrets(this->crypto, premaster, + this->session, this->server, chunk_from_thing(this->client_random), chunk_from_thing(this->server_random)); chunk_clear(&premaster); @@ -1042,34 +1072,52 @@ METHOD(tls_handshake_t, build, status_t, } METHOD(tls_handshake_t, cipherspec_changed, bool, - private_tls_peer_t *this) + private_tls_peer_t *this, bool inbound) { - if ((this->peer && this->state == STATE_VERIFY_SENT) || - (!this->peer && this->state == STATE_KEY_EXCHANGE_SENT)) + if (inbound) { - this->crypto->change_cipher(this->crypto, FALSE); - this->state = STATE_CIPHERSPEC_CHANGED_OUT; - return TRUE; + if (this->resume) + { + return this->state == STATE_HELLO_RECEIVED; + } + return this->state == STATE_FINISHED_SENT; + } + else + { + if (this->resume) + { + return this->state == STATE_FINISHED_RECEIVED; + } + if (this->peer) + { + return this->state == STATE_VERIFY_SENT; + } + return this->state == STATE_KEY_EXCHANGE_SENT; } - return FALSE; } -METHOD(tls_handshake_t, change_cipherspec, bool, - private_tls_peer_t *this) +METHOD(tls_handshake_t, change_cipherspec, void, + private_tls_peer_t *this, bool inbound) { - if (this->state == STATE_FINISHED_SENT) + this->crypto->change_cipher(this->crypto, inbound); + if (inbound) { - this->crypto->change_cipher(this->crypto, TRUE); this->state = STATE_CIPHERSPEC_CHANGED_IN; - return TRUE; } - return FALSE; + else + { + this->state = STATE_CIPHERSPEC_CHANGED_OUT; + } } METHOD(tls_handshake_t, finished, bool, private_tls_peer_t *this) { - return this->state == STATE_COMPLETE; + if (this->resume) + { + return this->state == STATE_FINISHED_SENT; + } + return this->state == STATE_FINISHED_RECEIVED; } METHOD(tls_handshake_t, destroy, void, @@ -1081,6 +1129,7 @@ METHOD(tls_handshake_t, destroy, void, this->server_auth->destroy(this->server_auth); free(this->hashsig.ptr); free(this->cert_types.ptr); + free(this->session.ptr); free(this); } diff --git a/src/libtls/tls_protection.h b/src/libtls/tls_protection.h index 99c94e935..05cf3df45 100644 --- a/src/libtls/tls_protection.h +++ b/src/libtls/tls_protection.h @@ -23,12 +23,12 @@ #include <library.h> +typedef struct tls_protection_t tls_protection_t; + #include "tls.h" #include "tls_alert.h" #include "tls_compression.h" -typedef struct tls_protection_t tls_protection_t; - /** * TLS record protocol protection layer. */ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index b8b67adf4..e3617dc9a 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -22,6 +22,10 @@ typedef struct private_tls_server_t private_tls_server_t; +/** + * Size of a session ID + */ +#define SESSION_ID_SIZE 16 typedef enum { STATE_INIT, @@ -121,6 +125,16 @@ struct private_tls_server_t { tls_version_t client_version; /** + * TLS session identifier + */ + chunk_t session; + + /** + * Do we resume a session? + */ + bool resume; + + /** * Hash and signature algorithms supported by peer */ chunk_t hashsig; @@ -199,6 +213,7 @@ static status_t process_client_hello(private_tls_server_t *this, bio_reader_t *extensions; tls_cipher_suite_t *suites; int count, i; + rng_t *rng; this->crypto->append_handshake(this->crypto, TLS_CLIENT_HELLO, reader->peek(reader)); @@ -228,7 +243,7 @@ static status_t process_client_hello(private_tls_server_t *this, extensions->destroy(extensions); return NEED_MORE; } - DBG1(DBG_TLS, "received TLS '%N' extension", + DBG2(DBG_TLS, "received TLS '%N' extension", tls_extension_names, extension); DBG3(DBG_TLS, "%B", &ext); switch (extension) @@ -249,6 +264,17 @@ static status_t process_client_hello(private_tls_server_t *this, memcpy(this->client_random, random.ptr, sizeof(this->client_random)); + htoun32(&this->server_random, time(NULL)); + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_TLS, "no suitable RNG found to generate server random"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4); + rng->destroy(rng); + if (!this->tls->set_version(this->tls, version)) { DBG1(DBG_TLS, "negotiated version %N not supported", @@ -256,24 +282,44 @@ static status_t process_client_hello(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); return NEED_MORE; } - count = ciphers.len / sizeof(u_int16_t); - suites = alloca(count * sizeof(tls_cipher_suite_t)); - DBG2(DBG_TLS, "received %d TLS cipher suites:", count); - for (i = 0; i < count; i++) + + this->client_version = version; + this->suite = this->crypto->resume_session(this->crypto, session, this->peer, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + if (this->suite) { - suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]); - DBG2(DBG_TLS, " %N", tls_cipher_suite_names, suites[i]); + this->session = chunk_clone(session); + this->resume = TRUE; + DBG1(DBG_TLS, "resumed %N using suite %N", + tls_version_names, this->tls->get_version(this->tls), + tls_cipher_suite_names, this->suite); } - - if (!select_suite_and_key(this, suites, count)) + else { - this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); - return NEED_MORE; + count = ciphers.len / sizeof(u_int16_t); + suites = alloca(count * sizeof(tls_cipher_suite_t)); + DBG2(DBG_TLS, "received %d TLS cipher suites:", count); + for (i = 0; i < count; i++) + { + suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]); + DBG2(DBG_TLS, " %N", tls_cipher_suite_names, suites[i]); + } + if (!select_suite_and_key(this, suites, count)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (rng) + { + rng->allocate_bytes(rng, SESSION_ID_SIZE, &this->session); + rng->destroy(rng); + } + DBG1(DBG_TLS, "negotiated %N using suite %N", + tls_version_names, this->tls->get_version(this->tls), + tls_cipher_suite_names, this->suite); } - DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", - tls_version_names, this->tls->get_version(this->tls), - tls_cipher_suite_names, this->suite); - this->client_version = version; this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -391,6 +437,7 @@ static status_t process_key_exchange_encrypted(private_tls_server_t *this, } this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), + this->session, this->peer, chunk_from_thing(this->client_random), chunk_from_thing(this->server_random)); @@ -439,6 +486,7 @@ static status_t process_key_exchange_dhe(private_tls_server_t *this, } this->crypto->derive_secrets(this->crypto, premaster, + this->session, this->peer, chunk_from_thing(this->client_random), chunk_from_thing(this->server_random)); chunk_clear(&premaster); @@ -576,10 +624,7 @@ METHOD(tls_handshake_t, process, status_t, expected = TLS_CERTIFICATE_VERIFY; break; } - else - { - return INVALID_STATE; - } + return INVALID_STATE; case STATE_CIPHERSPEC_CHANGED_IN: if (type == TLS_FINISHED) { @@ -605,27 +650,12 @@ METHOD(tls_handshake_t, process, status_t, static status_t send_server_hello(private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { - tls_version_t version; - rng_t *rng; - - htoun32(&this->server_random, time(NULL)); - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng) - { - DBG1(DBG_TLS, "no suitable RNG found to generate server random"); - this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); - return FAILED; - } - rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4); - rng->destroy(rng); - /* TLS version */ - version = this->tls->get_version(this->tls); - writer->write_uint16(writer, version); + writer->write_uint16(writer, this->tls->get_version(this->tls)); writer->write_data(writer, chunk_from_thing(this->server_random)); - /* session identifier => none, we don't support session resumption */ - writer->write_data8(writer, chunk_empty); + /* session identifier if we have one */ + writer->write_data8(writer, this->session); /* add selected TLS cipher suite */ writer->write_uint16(writer, this->suite); @@ -914,9 +944,8 @@ static status_t send_finished(private_tls_server_t *this, *type = TLS_FINISHED; this->state = STATE_FINISHED_SENT; - this->crypto->derive_eap_msk(this->crypto, - chunk_from_thing(this->client_random), - chunk_from_thing(this->server_random)); + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; } @@ -956,33 +985,52 @@ METHOD(tls_handshake_t, build, status_t, } METHOD(tls_handshake_t, cipherspec_changed, bool, - private_tls_server_t *this) + private_tls_server_t *this, bool inbound) { - if (this->state == STATE_FINISHED_RECEIVED) + if (inbound) { - this->crypto->change_cipher(this->crypto, FALSE); - this->state = STATE_CIPHERSPEC_CHANGED_OUT; - return TRUE; + if (this->resume) + { + return this->state == STATE_FINISHED_SENT; + } + if (this->peer) + { + return this->state == STATE_CERT_VERIFY_RECEIVED; + } + return this->state == STATE_KEY_EXCHANGE_RECEIVED; + } + else + { + if (this->resume) + { + return this->state == STATE_HELLO_SENT; + } + return this->state == STATE_FINISHED_RECEIVED; } return FALSE; } -METHOD(tls_handshake_t, change_cipherspec, bool, - private_tls_server_t *this) +METHOD(tls_handshake_t, change_cipherspec, void, + private_tls_server_t *this, bool inbound) { - if ((this->peer && this->state == STATE_CERT_VERIFY_RECEIVED) || - (!this->peer && this->state == STATE_KEY_EXCHANGE_RECEIVED)) + this->crypto->change_cipher(this->crypto, inbound); + if (inbound) { - this->crypto->change_cipher(this->crypto, TRUE); this->state = STATE_CIPHERSPEC_CHANGED_IN; - return TRUE; } - return FALSE; + else + { + this->state = STATE_CIPHERSPEC_CHANGED_OUT; + } } METHOD(tls_handshake_t, finished, bool, private_tls_server_t *this) { + if (this->resume) + { + return this->state == STATE_FINISHED_RECEIVED; + } return this->state == STATE_FINISHED_SENT; } @@ -995,6 +1043,7 @@ METHOD(tls_handshake_t, destroy, void, this->server_auth->destroy(this->server_auth); free(this->hashsig.ptr); free(this->curves.ptr); + free(this->session.ptr); free(this); } diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c index 691a8e79f..3abff596d 100644 --- a/src/libtls/tls_socket.c +++ b/src/libtls/tls_socket.c @@ -16,8 +16,20 @@ #include "tls_socket.h" #include <unistd.h> +#include <errno.h> #include <debug.h> +#include <threading/thread.h> + +/** + * Buffer size for plain side I/O + */ +#define PLAIN_BUF_SIZE 4096 + +/** + * Buffer size for encrypted side I/O + */ +#define CRYPTO_BUF_SIZE 4096 typedef struct private_tls_socket_t private_tls_socket_t; typedef struct private_tls_application_t private_tls_application_t; @@ -96,8 +108,8 @@ METHOD(tls_application_t, build, status_t, */ static bool exchange(private_tls_socket_t *this, bool wr) { - char buf[1024]; - ssize_t len; + char buf[CRYPTO_BUF_SIZE], *pos; + ssize_t len, out; int round = 0; for (round = 0; TRUE; round++) @@ -109,10 +121,18 @@ static bool exchange(private_tls_socket_t *this, bool wr) { case NEED_MORE: case ALREADY_DONE: - len = write(this->fd, buf, len); - if (len == -1) + pos = buf; + while (len) { - return FALSE; + out = write(this->fd, pos, len); + if (out == -1) + { + DBG1(DBG_TLS, "TLS crypto write error: %s", + strerror(errno)); + return FALSE; + } + len -= out; + pos += out; } continue; case INVALID_STATE: @@ -175,6 +195,81 @@ METHOD(tls_socket_t, write_, bool, return FALSE; } +METHOD(tls_socket_t, splice, bool, + private_tls_socket_t *this, int rfd, int wfd) +{ + char buf[PLAIN_BUF_SIZE], *pos; + fd_set set; + chunk_t data; + ssize_t len; + bool old; + + while (TRUE) + { + 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); + thread_cancelability(old); + if (len == -1) + { + DBG1(DBG_TLS, "TLS select error: %s", strerror(errno)); + return FALSE; + } + if (FD_ISSET(this->fd, &set)) + { + if (!read_(this, &data)) + { + DBG2(DBG_TLS, "TLS read error/disconnect"); + return TRUE; + } + pos = data.ptr; + while (data.len) + { + 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; + } + free(data.ptr); + } + if (FD_ISSET(rfd, &set)) + { + len = read(rfd, buf, sizeof(buf)); + if (len > 0) + { + if (!write_(this, chunk_create(buf, len))) + { + DBG1(DBG_TLS, "TLS write error"); + return FALSE; + } + } + else + { + if (len < 0) + { + DBG1(DBG_TLS, "TLS plain read error: %s", strerror(errno)); + return FALSE; + } + return TRUE; + } + } + } +} + +METHOD(tls_socket_t, get_fd, int, + private_tls_socket_t *this) +{ + return this->fd; +} + METHOD(tls_socket_t, destroy, void, private_tls_socket_t *this) { @@ -187,7 +282,7 @@ METHOD(tls_socket_t, destroy, void, * See header */ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, - identification_t *peer, int fd) + identification_t *peer, int fd, tls_cache_t *cache) { private_tls_socket_t *this; @@ -195,6 +290,8 @@ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, .public = { .read = _read_, .write = _write_, + .splice = _splice, + .get_fd = _get_fd, .destroy = _destroy, }, .app = { @@ -208,7 +305,7 @@ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, ); this->tls = tls_create(is_server, server, peer, TLS_PURPOSE_GENERIC, - &this->app.application); + &this->app.application, cache); if (!this->tls) { free(this); diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h index ac714a385..edd05fd29 100644 --- a/src/libtls/tls_socket.h +++ b/src/libtls/tls_socket.h @@ -55,6 +55,25 @@ struct tls_socket_t { bool (*write)(tls_socket_t *this, chunk_t data); /** + * Read/write plain data from file descriptor. + * + * This call is blocking, but a thread cancellation point. Data is + * exchanged until one of the sockets gets closed or an error occurs. + * + * @param rfd file descriptor to read plain data from + * @param wfd file descriptor to write plain data to + * @return TRUE if data exchanged successfully + */ + bool (*splice)(tls_socket_t *this, int rfd, int wfd); + + /** + * Get the underlying file descriptor passed to the constructor. + * + * @return file descriptor + */ + int (*get_fd)(tls_socket_t *this); + + /** * Destroy a tls_socket_t. */ void (*destroy)(tls_socket_t *this); @@ -67,9 +86,10 @@ struct tls_socket_t { * @param server server identity * @param peer client identity, NULL for no client authentication * @param fd socket to read/write from + * @param cache session cache to use, or NULL * @return TLS socket wrapper */ tls_socket_t *tls_socket_create(bool is_server, identification_t *server, - identification_t *peer, int fd); + identification_t *peer, int fd, tls_cache_t *cache); #endif /** TLS_SOCKET_H_ @}*/ diff --git a/src/libtnccs/Android.mk b/src/libtnccs/Android.mk index 0055fce34..a4bbc13f5 100644 --- a/src/libtnccs/Android.mk +++ b/src/libtnccs/Android.mk @@ -21,6 +21,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := libtnccs +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libtnccs/Makefile.am b/src/libtnccs/Makefile.am index 4f331c46b..449d32d92 100644 --- a/src/libtnccs/Makefile.am +++ b/src/libtnccs/Makefile.am @@ -13,4 +13,4 @@ tnc/imv/imv_recommendations.h tnc/imv/imv_recommendations.c \ tnc/tnccs/tnccs.h tnc/tnccs/tnccs.c \ tnc/tnccs/tnccs_manager.h tnc/tnccs/tnccs_manager.c - +EXTRA_DIST = Android.mk diff --git a/src/libtnccs/tnc/imc/imc.h b/src/libtnccs/tnc/imc/imc.h index ddedf714c..3ff7d5194 100644 --- a/src/libtnccs/tnc/imc/imc.h +++ b/src/libtnccs/tnc/imc/imc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2010-2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -40,11 +40,11 @@ struct imc_t { * the API version number to be used. It also supplies the IMC ID, an IMC * identifier that the IMC must use when calling TNC Client callback functions. * - * @param imcID IMC ID assigned by TNCC - * @param minVersion minimum API version supported by TNCC - * @param maxVersion maximum API version supported by TNCC - * @param OutActualVersion mutually supported API version number - * @return TNC result code + * @param imcID IMC ID assigned by TNCC + * @param minVersion minimum API version supported by TNCC + * @param maxVersion maximum API version supported by TNCC + * @param OutActualVersion mutually supported API version number + * @return TNC result code */ TNC_Result (*initialize)(TNC_IMCID imcID, TNC_Version minVersion, @@ -55,10 +55,10 @@ struct imc_t { * The TNC Client calls this function to inform the IMC that the state of * the network connection identified by connectionID has changed to newState. * - * @param imcID IMC ID assigned by TNCC - * @param connectionID network connection ID assigned by TNCC - * @param newState new network connection state - * @return TNC result code + * @param imcID IMC ID assigned by TNCC + * @param connectionID network connection ID assigned by TNCC + * @param newState new network connection state + * @return TNC result code */ TNC_Result (*notify_connection_change)(TNC_IMCID imcID, TNC_ConnectionID connectionID, @@ -68,9 +68,9 @@ struct imc_t { * The TNC Client calls this function to indicate that an Integrity Check * Handshake is beginning and solicit messages from IMCs for the first batch. * - * @param imcID IMC ID assigned by TNCC - * @param connectionID network connection ID assigned by TNCC - * @return TNC result code + * @param imcID IMC ID assigned by TNCC + * @param connectionID network connection ID assigned by TNCC + * @return TNC result code */ TNC_Result (*begin_handshake)(TNC_IMCID imcID, TNC_ConnectionID connectionID); @@ -81,12 +81,12 @@ struct imc_t { * the number of octets indicated by messageLength. The type of the message * is indicated by messageType. * - * @param imcID IMC ID assigned by TNCS - * @param connectionID network connection ID assigned by TNCC - * @param message reference to buffer containing message - * @param messageLength number of octets in message - * @param messageType message type of message - * @return TNC result code + * @param imcID IMC ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCC + * @param message reference to buffer containing message + * @param messageLength number of octets in message + * @param messageType message type of message + * @return TNC result code */ TNC_Result (*receive_message)(TNC_IMCID imcID, TNC_ConnectionID connectionID, @@ -95,13 +95,40 @@ struct imc_t { TNC_MessageType messageType); /** + * The TNC Client calls this function to deliver a message to the IMC. + * The message is contained in the buffer referenced by message and contains + * the number of octets indicated by messageLength. The type of the message + * is indicated by the message Vendor ID and message subtype. + * + * @param imcID IMC ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCC + * @param messageFlags message flags + * @param message reference to buffer containing message + * @param messageLength number of octets in message + * @param messageVendorID message Vendor ID + * @param messageSubtype message subtype + * @param sourceIMVID source IMV ID + * @param destinationIMCID destination IMC ID + * @return TNC result code + */ + TNC_Result (*receive_message_long)(TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_UInt32 messageFlags, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_VendorID messageVendorID, + TNC_MessageSubtype messageSubtype, + TNC_UInt32 sourceIMVID, + TNC_UInt32 destinationIMCID); + + /** * The TNC Client calls this function to notify IMCs that all IMV messages * received in a batch have been delivered and this is the IMC’s last chance * to send a message in the batch of IMC messages currently being collected. * - * @param imcID IMC ID assigned by TNCC - * @param connectionID network connection ID assigned by TNCC - * @return TNC result code + * @param imcID IMC ID assigned by TNCC + * @param connectionID network connection ID assigned by TNCC + * @return TNC result code */ TNC_Result (*batch_ending)(TNC_IMCID imcID, TNC_ConnectionID connectionID); @@ -110,8 +137,8 @@ struct imc_t { * The TNC Client calls this function to close down the IMC when all work is * complete or the IMC reports TNC_RESULT_FATAL. * - * @param imcID IMC ID assigned by TNCC - * @return TNC result code + * @param imcID IMC ID assigned by TNCC + * @return TNC result code */ TNC_Result (*terminate)(TNC_IMCID imcID); @@ -122,9 +149,9 @@ struct imc_t { * TNCS bind function. The IMV can then use the TNCS bind function to obtain * pointers to any other TNCS functions. * - * @param imcID IMC ID assigned by TNCC - * @param bindFunction pointer to TNC_TNCC_BindFunction - * @return TNC result code + * @param imcID IMC ID assigned by TNCC + * @param bindFunction pointer to TNC_TNCC_BindFunction + * @return TNC result code */ TNC_Result (*provide_bind_function)(TNC_IMCID imcID, TNC_TNCC_BindFunctionPointer bindFunction); @@ -132,40 +159,67 @@ struct imc_t { /** * Sets the ID of an imc_t object. * - * @param id IMC ID to be assigned + * @param id IMC ID to be assigned */ void (*set_id)(imc_t *this, TNC_IMCID id); /** * Returns the ID of an imc_t object. * - * @return assigned IMC ID + * @return assigned IMC ID */ TNC_IMCID (*get_id)(imc_t *this); /** + * Assign an additional ID to an imc_t object. + * + * @param id additional IMC ID to be assigned + */ + void (*add_id)(imc_t *this, TNC_IMCID id); + + /** + * Checks if the ID is assigned to the imc_t object. + * + * @return TRUE if IMC ID is assigned to imc_t object + */ + bool (*has_id)(imc_t *this, TNC_IMCID id); + + /** * Returns the name of an imc_t object. * - * @return name of IMC + * @return name of IMC */ char* (*get_name)(imc_t *this); /** * Sets the supported message types of an imc_t object. * - * @param supported_types list of messages type supported by IMC - * @param type_count number of supported message types + * @param supported_types list of messages type supported by IMC + * @param type_count number of supported message types */ void (*set_message_types)(imc_t *this, TNC_MessageTypeList supported_types, TNC_UInt32 type_count); /** + * Sets the supported long message types of an imc_t object. + * + * @param supported_vids list of vendor IDs supported by IMC + * @param supported_subtypes list of messages type supported by IMC + * @param type_count number of supported message types + */ + void (*set_message_types_long)(imc_t *this, TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count); + + /** * Check if the IMC supports a given message type. * - * @param message_type message type - * @return TRUE if supported + * @param msg_vid message vendor ID + * @param msg_subtype message subtype + * @return TRUE if supported */ - bool (*type_supported)(imc_t *this, TNC_MessageType message_type); + bool (*type_supported)(imc_t *this, TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype); /** * Destroys an imc_t object. diff --git a/src/libtnccs/tnc/imc/imc_manager.h b/src/libtnccs/tnc/imc/imc_manager.h index 396964100..25e0efe9d 100644 --- a/src/libtnccs/tnc/imc/imc_manager.h +++ b/src/libtnccs/tnc/imc/imc_manager.h @@ -66,6 +66,15 @@ struct imc_manager_t { bool (*is_registered)(imc_manager_t *this, TNC_IMCID id); /** + * Reserve an additional ID for an IMC + * + * @param id ID of IMC instance + * @param new_id reserved ID assigned to IMC + * @return TRUE if primary IMC ID was used + */ + bool (*reserve_id)(imc_manager_t *this, TNC_IMCID id, TNC_UInt32 *new_id); + + /** * Return the preferred language for recommendations * * @return preferred language string @@ -84,17 +93,17 @@ struct imc_manager_t { /** * Begin a handshake between the IMCs and a connection * - * @param id connection ID + * @param id connection ID */ void (*begin_handshake)(imc_manager_t *this, TNC_ConnectionID id); /** * Sets the supported message types reported by a given IMC * - * @param id ID of reporting IMC - * @param supported_types list of messages type supported by IMC - * @param type_count number of supported message types - * @return TNC result code + * @param id ID of reporting IMC + * @param supported_types list of messages type supported by IMC + * @param type_count number of supported message types + * @return TNC result code */ TNC_Result (*set_message_types)(imc_manager_t *this, TNC_IMCID id, @@ -102,18 +111,41 @@ struct imc_manager_t { TNC_UInt32 type_count); /** + * Sets the supported long message types reported by a given IMC + * + * @param id ID of reporting IMC + * @param supported_vids list of vendor IDs supported by IMC + * @param supported_subtypes list of messages type supported by IMC + * @param type_count number of supported message types + * @return TNC result code + */ + TNC_Result (*set_message_types_long)(imc_manager_t *this, + TNC_IMCID id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count); + + /** * Delivers a message to interested IMCs. * - * @param connection_id ID of connection over which message was received - * @param message message - * @param message_len message length - * @param message_type message type + * @param connection_id connection ID + * @param excl exclusive message flag + * @param msg message + * @param msg_len message length + * @param msg_vid message Vendor ID + * @param msg_subtype message subtype + * @param src_imv_id source IMV ID + * @param dst_imc_id destination IMC ID */ void (*receive_message)(imc_manager_t *this, TNC_ConnectionID connection_id, - TNC_BufferReference message, - TNC_UInt32 message_len, - TNC_MessageType message_type); + bool excl, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imv_id, + TNC_UInt32 dst_imc_id); /** * Notify all IMCs that all IMV messages received in a batch have been diff --git a/src/libtnccs/tnc/imv/imv.h b/src/libtnccs/tnc/imv/imv.h index df338d40a..3716532d6 100644 --- a/src/libtnccs/tnc/imv/imv.h +++ b/src/libtnccs/tnc/imv/imv.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2010-2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -40,11 +40,11 @@ struct imv_t { * the API version number to be used. It also supplies the IMV ID, an IMV * identifier that the IMV must use when calling TNC Server callback functions. * - * @param imvID IMV ID assigned by TNCS - * @param minVersion minimum API version supported - * @param maxVersion maximum API version supported by TNCS - * @param OutActualVersion mutually supported API version number - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @param minVersion minimum API version supported + * @param maxVersion maximum API version supported by TNCS + * @param OutActualVersion mutually supported API version number + * @return TNC result code */ TNC_Result (*initialize)(TNC_IMVID imvID, TNC_Version minVersion, @@ -55,10 +55,10 @@ struct imv_t { * The TNC Server calls this function to inform the IMV that the state of * the network connection identified by connectionID has changed to newState. * - * @param imvID IMV ID assigned by TNCS - * @param connectionID network connection ID assigned by TNCS - * @param newState new network connection state - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCS + * @param newState new network connection state + * @return TNC result code */ TNC_Result (*notify_connection_change)(TNC_IMVID imvID, TNC_ConnectionID connectionID, @@ -69,9 +69,9 @@ struct imv_t { * Handshake (after all IMC-IMV messages have been delivered) to solicit * recommendations from IMVs that have not yet provided a recommendation. * - * @param imvID IMV ID assigned by TNCS - * @param connectionID network connection ID assigned by TNCS - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCS + * @return TNC result code */ TNC_Result (*solicit_recommendation)(TNC_IMVID imvID, TNC_ConnectionID connectionID); @@ -82,12 +82,12 @@ struct imv_t { * the number of octets indicated by messageLength. The type of the message * is indicated by messageType. * - * @param imvID IMV ID assigned by TNCS - * @param connectionID network connection ID assigned by TNCS - * @param message reference to buffer containing message - * @param messageLength number of octets in message - * @param messageType message type of message - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCS + * @param message reference to buffer containing message + * @param messageLength number of octets in message + * @param messageType message type of message + * @return TNC result code */ TNC_Result (*receive_message)(TNC_IMVID imvID, TNC_ConnectionID connectionID, @@ -96,13 +96,40 @@ struct imv_t { TNC_MessageType messageType); /** + * The TNC Server calls this function to deliver a message to the IMV. + * The message is contained in the buffer referenced by message and contains + * the number of octets indicated by messageLength. The type of the message + * is indicated by the message Vendor ID and message subtype. + * + * @param imvID IMV ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCS + * @param messageFlags message flags + * @param message reference to buffer containing message + * @param messageLength number of octets in message + * @param messageVendorID message Vendor ID + * @param messageSubtype message subtype + * @param sourceIMCID source IMC ID + * @param destinationIMVID destination IMV ID + * @return TNC result code + */ + TNC_Result (*receive_message_long)(TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_UInt32 messageFlags, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_VendorID messageVendorID, + TNC_MessageSubtype messageSubtype, + TNC_UInt32 sourceIMCID, + TNC_UInt32 destinationIMVID); + + /** * The TNC Server calls this function to notify IMVs that all IMC messages * received in a batch have been delivered and this is the IMV’s last chance * to send a message in the batch of IMV messages currently being collected. * - * @param imvID IMV ID assigned by TNCS - * @param connectionID network connection ID assigned by TNCS - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @param connectionID network connection ID assigned by TNCS + * @return TNC result code */ TNC_Result (*batch_ending)(TNC_IMVID imvID, TNC_ConnectionID connectionID); @@ -110,8 +137,8 @@ struct imv_t { /** * The TNC Server calls this function to close down the IMV. * - * @param imvID IMV ID assigned by TNCS - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @return TNC result code */ TNC_Result (*terminate)(TNC_IMVID imvID); @@ -122,9 +149,9 @@ struct imv_t { * TNCS bind function. The IMV can then use the TNCS bind function to obtain * pointers to any other TNCS functions. * - * @param imvID IMV ID assigned by TNCS - * @param bindFunction pointer to TNC_TNCS_BindFunction - * @return TNC result code + * @param imvID IMV ID assigned by TNCS + * @param bindFunction pointer to TNC_TNCS_BindFunction + * @return TNC result code */ TNC_Result (*provide_bind_function)(TNC_IMVID imvID, TNC_TNCS_BindFunctionPointer bindFunction); @@ -132,40 +159,67 @@ struct imv_t { /** * Sets the ID of an imv_t object. * - * @param id IMV ID to be assigned + * @param id IMV ID to be assigned */ void (*set_id)(imv_t *this, TNC_IMVID id); /** * Returns the ID of an imv_t object. * - * @return IMV ID assigned by TNCS + * @return IMV ID assigned by TNCS */ TNC_IMVID (*get_id)(imv_t *this); /** + * Assign an additional ID to an imv_t object. + * + * @param id additional IMV ID to be assigned + */ + void (*add_id)(imv_t *this, TNC_IMVID id); + + /** + * Checks if the ID is assigned to the imv_t object. + * + * @return TRUE if IMV ID is assigned to imv_t object + */ + bool (*has_id)(imv_t *this, TNC_IMVID id); + + /** * Returns the name of an imv_t object. * - * @return name of IMV + * @return name of IMV */ char* (*get_name)(imv_t *this); /** * Sets the supported message types of an imv_t object. * - * @param supported_types list of messages type supported by IMV - * @param type_count number of supported message types + * @param supported_types list of messages type supported by IMV + * @param type_count number of supported message types */ void (*set_message_types)(imv_t *this, TNC_MessageTypeList supported_types, TNC_UInt32 type_count); /** + * Sets the supported long message types of an imv_t object. + * + * @param supported_vids list of vendor IDs supported by IMC + * @param supported_subtypes list of messages type supported by IMC + * @param type_count number of supported message types + */ + void (*set_message_types_long)(imv_t *this, TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count); + + /** * Check if the IMV supports a given message type. * - * @param message_type message type - * @return TRUE if supported + * @param msg_vid message vendor ID + * @param msg_subtype message subtype + * @return TRUE if supported */ - bool (*type_supported)(imv_t *this, TNC_MessageType message_type); + bool (*type_supported)(imv_t *this, TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype); /** * Destroys an imv_t object. diff --git a/src/libtnccs/tnc/imv/imv_manager.h b/src/libtnccs/tnc/imv/imv_manager.h index caa25e857..43f40973c 100644 --- a/src/libtnccs/tnc/imv/imv_manager.h +++ b/src/libtnccs/tnc/imv/imv_manager.h @@ -67,6 +67,14 @@ struct imv_manager_t { */ bool (*is_registered)(imv_manager_t *this, TNC_IMVID id); + /** + * Reserve an additional ID for an IMV + * + * @param id ID of IMV instance + * @param new_id reserved ID assigned to IMV + * @return TRUE if primary IMV ID was used + */ + bool (*reserve_id)(imv_manager_t *this, TNC_IMVID id, TNC_UInt32 *new_id); /** * Get the configured recommendation policy @@ -106,10 +114,10 @@ struct imv_manager_t { /** * Sets the supported message types reported by a given IMV * - * @param id ID of reporting IMV - * @param supported_types list of messages type supported by IMV - * @param type_count number of supported message types - * @return TNC result code + * @param id ID of reporting IMV + * @param supported_types list of messages type supported by IMV + * @param type_count number of supported message types + * @return TNC result code */ TNC_Result (*set_message_types)(imv_manager_t *this, TNC_IMVID id, @@ -117,25 +125,48 @@ struct imv_manager_t { TNC_UInt32 type_count); /** + * Sets the supported long message types reported by a given IMV + * + * @param id ID of reporting IMV + * @param supported_vids list of vendor IDs supported by IMV + * @param supported_subtypes list of messages type supported by IMV + * @param type_count number of supported message types + * @return TNC result code + */ + TNC_Result (*set_message_types_long)(imv_manager_t *this, + TNC_IMVID id, + TNC_VendorIDList supported_vids, + TNC_MessageSubtypeList supported_subtypes, + TNC_UInt32 type_count); + + /** * Solicit recommendations from IMVs that have not yet provided one * - * @param id connection ID + * @param id connection ID */ void (*solicit_recommendation)(imv_manager_t *this, TNC_ConnectionID id); /** * Delivers a message to interested IMVs. * - * @param connection_id ID of connection over which message was received - * @param message message - * @param message_len message length - * @param message_type message type + * @param connection_id connection ID + * @param excl exclusive message flag + * @param msg message + * @param msg_len message length + * @param msg_vid message Vendor ID + * @param msg_subtype message subtype + * @param src_imc_id source IMC ID + * @param dst_imv_id destination IMV ID */ void (*receive_message)(imv_manager_t *this, TNC_ConnectionID connection_id, - TNC_BufferReference message, - TNC_UInt32 message_len, - TNC_MessageType message_type); + bool excl, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype, + TNC_UInt32 src_imc_id, + TNC_UInt32 dst_imv_id); /** * Notify all IMVs that all IMC messages received in a batch have been diff --git a/src/libtnccs/tnc/tnccs/tnccs.h b/src/libtnccs/tnc/tnccs/tnccs.h index 4bbab5bd3..c3020d7c3 100644 --- a/src/libtnccs/tnc/tnccs/tnccs.h +++ b/src/libtnccs/tnc/tnccs/tnccs.h @@ -63,15 +63,20 @@ typedef tnccs_t *(*tnccs_constructor_t)(bool is_server); * * @param imc_id ID of IMC or TNC_IMCID_ANY * @param imc_id ID of IMV or TNC_IMVID_ANY + * @param msg_flags message flags * @param msg message to be added * @param msg_len message length - * @param msg_type message type - * @return result code + * @param msg_vid message vendor ID + * @param msg_subtype message subtype + * @return return code */ -typedef TNC_Result (*tnccs_send_message_t)(tnccs_t* tncss, TNC_IMCID imc_id, - TNC_IMVID imv_id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type); +typedef TNC_Result (*tnccs_send_message_t)(tnccs_t* tncss, + TNC_IMCID imc_id, + TNC_IMVID imv_id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype); #endif /** TNCCS_H_ @}*/ diff --git a/src/libtnccs/tnc/tnccs/tnccs_manager.h b/src/libtnccs/tnc/tnccs/tnccs_manager.h index 26b0fa17c..9ca450468 100644 --- a/src/libtnccs/tnc/tnccs/tnccs_manager.h +++ b/src/libtnccs/tnc/tnccs/tnccs_manager.h @@ -66,13 +66,15 @@ struct tnccs_manager_t { * callback function for adding a message to a TNCCS batch and create * an empty set for collecting IMV recommendations * + * @param type TNCCS protocol type * @param tnccs TNCCS connection instance * @param send_message TNCCS callback function * @param request_handshake_retry pointer to boolean variable * @param recs pointer to IMV recommendation set * @return assigned connection ID */ - TNC_ConnectionID (*create_connection)(tnccs_manager_t *this, tnccs_t *tnccs, + TNC_ConnectionID (*create_connection)(tnccs_manager_t *this, + tnccs_type_t type, tnccs_t *tnccs, tnccs_send_message_t send_message, bool *request_handshake_retry, recommendations_t **recs); @@ -106,17 +108,22 @@ struct tnccs_manager_t { * @param imc_id ID of IMC or TNC_IMCID_ANY * @param imv_id ID of IMV or TNC_IMVID_ANY * @param id ID of target connection + * @param msg_flags message flags * @param msg message to be added * @param msg_len message length - * @param msg_type message type + * @param msg_vid message vendor ID + * @param msg_subtype message subtype * @return return code */ - TNC_Result (*send_message)(tnccs_manager_t *this, TNC_IMCID imc_id, - TNC_IMVID imv_id, - TNC_ConnectionID id, - TNC_BufferReference msg, - TNC_UInt32 msg_len, - TNC_MessageType msg_type); + TNC_Result (*send_message)(tnccs_manager_t *this, + TNC_IMCID imc_id, + TNC_IMVID imv_id, + TNC_ConnectionID id, + TNC_UInt32 msg_flags, + TNC_BufferReference msg, + TNC_UInt32 msg_len, + TNC_VendorID msg_vid, + TNC_MessageSubtype msg_subtype); /** * Deliver an IMV Action Recommendation and IMV Evaluation Result to the TNCS @@ -137,35 +144,37 @@ struct tnccs_manager_t { * Get the value of an attribute associated with a connection or with the * TNCS as a whole. * - * @param imv_id ID of the IMV requesting the attribute + * @param is_imc TRUE if IMC, FALSE if IMV + * @param imcv_id ID of the IMC/IMV requesting the attribute * @param id ID of target connection * @param attribute_id ID of the requested attribute * @param buffer_len length of the buffer in bytes * @param buffer pointer to the buffer - * @param out_value_len actual length of the returned attribute + * @param value_len actual length of the returned attribute * @return return code */ - TNC_Result (*get_attribute)(tnccs_manager_t *this, - TNC_IMVID imv_id, + TNC_Result (*get_attribute)(tnccs_manager_t *this, bool is_imc, + TNC_UInt32 imcv_id, TNC_ConnectionID id, TNC_AttributeID attribute_id, TNC_UInt32 buffer_len, TNC_BufferReference buffer, - TNC_UInt32 *out_value_len); + TNC_UInt32 *value_len); /** * Set the value of an attribute associated with a connection or with the * TNCS as a whole. * - * @param imv_id ID of the IMV setting the attribute + * @param is_imc TRUE if IMC, FALSE if IMV + * @param imcv_id ID of the IMC/IMV setting the attribute * @param id ID of target connection * @param attribute_id ID of the attribute to be set * @param buffer_len length of the buffer in bytes * @param buffer pointer to the buffer * @return return code */ - TNC_Result (*set_attribute)(tnccs_manager_t *this, - TNC_IMVID imv_id, + TNC_Result (*set_attribute)(tnccs_manager_t *this, bool is_imc, + TNC_UInt32 imcv_id, TNC_ConnectionID id, TNC_AttributeID attribute_id, TNC_UInt32 buffer_len, diff --git a/src/libtncif/Android.mk b/src/libtncif/Android.mk index 91cfa8354..ef406dd59 100644 --- a/src/libtncif/Android.mk +++ b/src/libtncif/Android.mk @@ -16,6 +16,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := libtncif +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/libtncif/Makefile.am b/src/libtncif/Makefile.am index cfaba912a..cc262ffca 100644 --- a/src/libtncif/Makefile.am +++ b/src/libtncif/Makefile.am @@ -5,3 +5,5 @@ noinst_LTLIBRARIES = libtncif.la libtncif_la_SOURCES = \ tncif.h tncifimc.h tncifimv.h tncif_names.h tncif_names.c \ tncif_pa_subtypes.h tncif_pa_subtypes.c + +EXTRA_DIST = Android.mk diff --git a/src/libtncif/tncif.h b/src/libtncif/tncif.h index 3a889962e..f904bc600 100644 --- a/src/libtncif/tncif.h +++ b/src/libtncif/tncif.h @@ -1,22 +1,25 @@ /* tncif.h * - * Trusted Network Connect IF-IMV API version 1.20 + * Trusted Network Connect IF-IMC/IMV API version 1.30 * Microsoft Windows DLL Platform Binding C Header - * February 5, 2007 + * October 14, 2011 * - * Copyright(c) 2005-2007, Trusted Computing Group, Inc. All rights + * Common definitions for IF-IMC and IF-IMV + * extracted from tncifimc.h and tncifimv.h + * + * Copyright(c) 2005-2011, Trusted Computing Group, Inc. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * - Redistributions of source code must retain the above copyright + * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright + * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. - * - Neither the name of the Trusted Computing Group nor the names of + * o Neither the name of the Trusted Computing Group nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. @@ -41,32 +44,38 @@ * Any marks and brands contained herein are the property of their * respective owners. * - * Trusted Network Connect IF-IMC/IF-IMV API version 1.00 Revision 3 - * Microsoft Windows DLL Platform Binding C Header - * Common definitions for IF-IMC and IF-IMV - * extracted from tncifimc.h and tncifimv.h - * Feb 12, 2007 + */ + +/** + * @defgroup tncif tncif + * @{ @ingroup libtncif */ #ifndef TNCIF_H_ #define TNCIF_H_ /* Basic Types */ + typedef unsigned long TNC_UInt32; typedef unsigned char *TNC_BufferReference; /* Derived Types */ + typedef TNC_UInt32 TNC_ConnectionID; typedef TNC_UInt32 TNC_ConnectionState; typedef TNC_UInt32 TNC_RetryReason; typedef TNC_UInt32 TNC_MessageType; typedef TNC_MessageType *TNC_MessageTypeList; typedef TNC_UInt32 TNC_VendorID; +typedef TNC_VendorID *TNC_VendorIDList; typedef TNC_UInt32 TNC_MessageSubtype; +typedef TNC_MessageSubtype *TNC_MessageSubtypeList; typedef TNC_UInt32 TNC_Version; typedef TNC_UInt32 TNC_Result; +typedef TNC_UInt32 TNC_AttributeID; /* Result Codes */ + #define TNC_RESULT_SUCCESS 0 #define TNC_RESULT_NOT_INITIALIZED 1 #define TNC_RESULT_ALREADY_INITIALIZED 2 @@ -78,11 +87,17 @@ typedef TNC_UInt32 TNC_Result; #define TNC_RESULT_ILLEGAL_OPERATION 8 #define TNC_RESULT_OTHER 9 #define TNC_RESULT_FATAL 10 +#define TNC_RESULT_EXCEEDED_MAX_ROUND_TRIPS 0x00559700 +#define TNC_RESULT_EXCEEDED_MAX_MESSAGE_SIZE 0x00559701 +#define TNC_RESULT_NO_LONG_MESSAGE_TYPES 0x00559702 +#define TNC_RESULT_NO_SOH_SUPPORT 0x00559703 /* Network Connection ID Values */ + #define TNC_CONNECTIONID_ANY 0xFFFFFFFF /* Network Connection State Values */ + #define TNC_CONNECTION_STATE_CREATE 0 #define TNC_CONNECTION_STATE_HANDSHAKE 1 #define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 @@ -90,10 +105,38 @@ typedef TNC_UInt32 TNC_Result; #define TNC_CONNECTION_STATE_ACCESS_NONE 4 #define TNC_CONNECTION_STATE_DELETE 5 +/* IMC/IMV ID Values */ + +#define TNC_IMVID_ANY ((TNC_UInt32) 0xffff) +#define TNC_IMCID_ANY ((TNC_UInt32) 0xffff) + /* Vendor ID Values */ + #define TNC_VENDORID_TCG 0 +#define TNC_VENDORID_TCG_NEW 0x005597 #define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) + /* Message Subtype Values */ + #define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff) +/* Message Flags Values */ + +#define TNC_MESSAGE_FLAGS_EXCLUSIVE ((TNC_UInt32) 0x80000000) + +/* Message Attribute ID Values */ + +#define TNC_ATTRIBUTEID_PREFERRED_LANGUAGE ((TNC_AttributeID) 0x00000001) +#define TNC_ATTRIBUTEID_MAX_ROUND_TRIPS ((TNC_AttributeID) 0x00559700) +#define TNC_ATTRIBUTEID_MAX_MESSAGE_SIZE ((TNC_AttributeID) 0x00559701) +#define TNC_ATTRIBUTEID_DHPN ((TNC_AttributeID) 0x00559702) +#define TNC_ATTRIBUTEID_HAS_LONG_TYPES ((TNC_AttributeID) 0x00559703) +#define TNC_ATTRIBUTEID_HAS_EXCLUSIVE ((TNC_AttributeID) 0x00559704) +#define TNC_ATTRIBUTEID_HAS_SOH ((TNC_AttributeID) 0x00559705) +#define TNC_ATTRIBUTEID_IFTNCCS_PROTOCOL ((TNC_AttributeID) 0x0055970A) +#define TNC_ATTRIBUTEID_IFTNCCS_VERSION ((TNC_AttributeID) 0x0055970B) +#define TNC_ATTRIBUTEID_IFT_PROTOCOL ((TNC_AttributeID) 0x0055970C) +#define TNC_ATTRIBUTEID_IFT_VERSION ((TNC_AttributeID) 0x0055970D) +#define TNC_ATTRIBUTEID_TLS_UNIQUE ((TNC_AttributeID) 0x0055970E) + #endif /** TNCIF_H_ @}*/ diff --git a/src/libtncif/tncif_names.h b/src/libtncif/tncif_names.h index a7c9e7be3..9b50a34e9 100644 --- a/src/libtncif/tncif_names.h +++ b/src/libtncif/tncif_names.h @@ -12,6 +12,16 @@ * for more details. */ +/** + * @defgroup libtncif libtncif + * + * @addtogroup libtncif + * TNC interface definitions + * + * @defgroup tncif_names tncif_names + * @{ @ingroup libtncif + */ + #ifndef TNCIF_NAMES_H_ #define TNCIF_NAMES_H_ diff --git a/src/libtncif/tncif_pa_subtypes.c b/src/libtncif/tncif_pa_subtypes.c index de857e1ce..d15a1c864 100644 --- a/src/libtncif/tncif_pa_subtypes.c +++ b/src/libtncif/tncif_pa_subtypes.c @@ -16,7 +16,7 @@ #include "tncif_pa_subtypes.h" -ENUM(pa_subtype_ietf_names, PA_SUBTYPE_IETF_TESTING, PA_SUBTYPE_IETF_NEA_CLIENT, +ENUM_BEGIN(pa_subtype_ietf_names, PA_SUBTYPE_IETF_TESTING, PA_SUBTYPE_IETF_NEA_CLIENT, "Testing", "Operating System", "Anti-Virus", @@ -27,10 +27,20 @@ ENUM(pa_subtype_ietf_names, PA_SUBTYPE_IETF_TESTING, PA_SUBTYPE_IETF_NEA_CLIENT, "VPN", "NEA Client" ); +ENUM_NEXT(pa_subtype_ietf_names, PA_SUBTYPE_IETF_ANY, PA_SUBTYPE_IETF_ANY, + PA_SUBTYPE_IETF_NEA_CLIENT, + "ANY" +); +ENUM_END(pa_subtype_ietf_names, PA_SUBTYPE_IETF_ANY); -ENUM(pa_subtype_tcg_names, PA_SUBTYPE_TCG_PTS, PA_SUBTYPE_TCG_PTS, +ENUM_BEGIN(pa_subtype_tcg_names, PA_SUBTYPE_TCG_PTS, PA_SUBTYPE_TCG_PTS, "PTS" ); +ENUM_NEXT(pa_subtype_tcg_names, PA_SUBTYPE_TCG_ANY, PA_SUBTYPE_TCG_ANY, + PA_SUBTYPE_TCG_PTS, + "ANY" +); +ENUM_END(pa_subtype_tcg_names, PA_SUBTYPE_TCG_ANY); ENUM_BEGIN(pa_subtype_fhh_names, PA_SUBTYPE_FHH_HOSTSCANNER, PA_SUBTYPE_FHH_DUMMY, "HostScanner", @@ -45,12 +55,21 @@ ENUM_NEXT(pa_subtype_fhh_names, PA_SUBTYPE_FHH_CLAMAV, PA_SUBTYPE_FHH_CLAMAV, PA_SUBTYPE_FHH_ATTESTATION, "ClamAV" ); -ENUM_END(pa_subtype_fhh_names, PA_SUBTYPE_FHH_CLAMAV); +ENUM_NEXT(pa_subtype_fhh_names, PA_SUBTYPE_FHH_ANY, PA_SUBTYPE_FHH_ANY, + PA_SUBTYPE_FHH_CLAMAV, + "ANY" +); +ENUM_END(pa_subtype_fhh_names, PA_SUBTYPE_FHH_ANY); -ENUM(pa_subtype_ita_names, PA_SUBTYPE_ITA_TEST, PA_SUBTYPE_ITA_SCANNER, +ENUM_BEGIN(pa_subtype_ita_names, PA_SUBTYPE_ITA_TEST, PA_SUBTYPE_ITA_SCANNER, "Test", "Scanner" ); +ENUM_NEXT(pa_subtype_ita_names, PA_SUBTYPE_ITA_ANY, PA_SUBTYPE_ITA_ANY, + PA_SUBTYPE_ITA_SCANNER, + "ANY" +); +ENUM_END(pa_subtype_ita_names, PA_SUBTYPE_ITA_ANY); /** * See header diff --git a/src/libtncif/tncif_pa_subtypes.h b/src/libtncif/tncif_pa_subtypes.h index 771971338..0be495bfc 100644 --- a/src/libtncif/tncif_pa_subtypes.h +++ b/src/libtncif/tncif_pa_subtypes.h @@ -12,6 +12,11 @@ * for more details. */ +/** + * @defgroup tncif_pa_subtypes tncif_pa_subtypes + * @{ @ingroup libtncif + */ + #ifndef TNCIF_PA_SUBTYPES_H_ #define TNCIF_PA_SUBTYPES_H_ @@ -27,15 +32,16 @@ typedef enum pa_subtype_ita_t pa_subtype_ita_t; * PA-TNC IETF Standard Subtypes as defined in section 3.5 of RFC 5792 */ enum pa_subtype_ietf_t { - PA_SUBTYPE_IETF_TESTING = 0, - PA_SUBTYPE_IETF_OPERATING_SYSTEM = 1, - PA_SUBTYPE_IETF_ANTI_VIRUS = 2, - PA_SUBTYPE_IETF_ANTI_SPYWARE = 3, - PA_SUBTYPE_IETF_ANTI_MALWARE = 4, - PA_SUBTYPE_IETF_FIREWALL = 5, - PA_SUBTYPE_IETF_IDPS = 6, - PA_SUBTYPE_IETF_VPN = 7, - PA_SUBTYPE_IETF_NEA_CLIENT = 8 + PA_SUBTYPE_IETF_TESTING = 0x00, + PA_SUBTYPE_IETF_OPERATING_SYSTEM = 0x01, + PA_SUBTYPE_IETF_ANTI_VIRUS = 0x02, + PA_SUBTYPE_IETF_ANTI_SPYWARE = 0x03, + PA_SUBTYPE_IETF_ANTI_MALWARE = 0x04, + PA_SUBTYPE_IETF_FIREWALL = 0x05, + PA_SUBTYPE_IETF_IDPS = 0x06, + PA_SUBTYPE_IETF_VPN = 0x07, + PA_SUBTYPE_IETF_NEA_CLIENT = 0x08, + PA_SUBTYPE_IETF_ANY = 0xff }; /** @@ -47,7 +53,8 @@ extern enum_name_t *pa_subtype_ietf_names; * PA-TNC TCG Subtypes */ enum pa_subtype_tcg_t { - PA_SUBTYPE_TCG_PTS = 1 + PA_SUBTYPE_TCG_PTS = 0x01, + PA_SUBTYPE_TCG_ANY = 0xff }; /** @@ -59,11 +66,12 @@ extern enum_name_t *pa_subtype_tcg_names; * PA-TNC FHH Subtypes */ enum pa_subtype_fhh_t { - PA_SUBTYPE_FHH_HOSTSCANNER = 0x30, - PA_SUBTYPE_FHH_DUMMY = 0x31, - PA_SUBTYPE_FHH_PLATID = 0x33, - PA_SUBTYPE_FHH_ATTESTATION = 0x34, - PA_SUBTYPE_FHH_CLAMAV = 0x41 + PA_SUBTYPE_FHH_HOSTSCANNER = 0x30, + PA_SUBTYPE_FHH_DUMMY = 0x31, + PA_SUBTYPE_FHH_PLATID = 0x33, + PA_SUBTYPE_FHH_ATTESTATION = 0x34, + PA_SUBTYPE_FHH_CLAMAV = 0x41, + PA_SUBTYPE_FHH_ANY = 0xff }; /** @@ -75,8 +83,9 @@ extern enum_name_t *pa_subtype_fhh_names; * PA-TNC ITA-HSR Subtypes */ enum pa_subtype_ita_t { - PA_SUBTYPE_ITA_TEST = 1, - PA_SUBTYPE_ITA_SCANNER = 2 + PA_SUBTYPE_ITA_TEST = 0x01, + PA_SUBTYPE_ITA_SCANNER = 0x02, + PA_SUBTYPE_ITA_ANY = 0xff }; /** diff --git a/src/libtncif/tncifimc.h b/src/libtncif/tncifimc.h index c6ddabd45..45af913df 100644 --- a/src/libtncif/tncifimc.h +++ b/src/libtncif/tncifimc.h @@ -1,22 +1,22 @@ /* tncifimc.h * - * Trusted Network Connect IF-IMC API version 1.20 Revision 8 + * Trusted Network Connect IF-IMC API version 1.30 * Microsoft Windows DLL Platform Binding C Header - * February 5, 2007 + * October 14, 2011 * - * Copyright(c) 2005-2007, Trusted Computing Group, Inc. All rights + * Copyright(c) 2005-2011, Trusted Computing Group, Inc. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * - Redistributions of source code must retain the above copyright + * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright + * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. - * - Neither the name of the Trusted Computing Group nor the names of + * o Neither the name of the Trusted Computing Group nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. @@ -45,7 +45,7 @@ /** * @defgroup tncifimc tncifimc - * @{ @ingroup tnc + * @{ @ingroup libtncif */ #ifndef TNCIFIMC_H_ @@ -53,6 +53,16 @@ #include "tncif.h" +#ifdef WIN32 +#ifdef TNC_IMC_EXPORTS +#define TNC_IMC_API __declspec(dllexport) +#else +#define TNC_IMC_API __declspec(dllimport) +#endif +#else +#define TNC_IMC_API +#endif + /* Derived Types */ typedef TNC_UInt32 TNC_IMCID; @@ -77,6 +87,22 @@ typedef TNC_Result (*TNC_IMC_ReceiveMessagePointer)( TNC_BufferReference message, TNC_UInt32 messageLength, TNC_MessageType messageType); +typedef TNC_Result (*TNC_IMC_ReceiveMessageSOHPointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference sohrReportEntry, + TNC_UInt32 sohrRELength, + TNC_MessageType systemHealthID); +typedef TNC_Result (*TNC_IMC_ReceiveMessageLongPointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_UInt32 messageFlags, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_VendorID messageVendorID, + TNC_MessageSubtype messageSubtype, + TNC_UInt32 sourceIMVID, + TNC_UInt32 destinationIMCID); typedef TNC_Result (*TNC_IMC_BatchEndingPointer)( TNC_IMCID imcID, TNC_ConnectionID connectionID); @@ -86,16 +112,51 @@ typedef TNC_Result (*TNC_TNCC_ReportMessageTypesPointer)( TNC_IMCID imcID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount); +typedef TNC_Result (*TNC_TNCC_ReportMessageTypesLongPointer)( + TNC_IMCID imcID, + TNC_VendorIDList supportedVendorIDs, + TNC_MessageSubtypeList supportedSubtypes, + TNC_UInt32 typeCount); typedef TNC_Result (*TNC_TNCC_SendMessagePointer)( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference message, TNC_UInt32 messageLength, TNC_MessageType messageType); +typedef TNC_Result (*TNC_TNCC_SendMessageSOHPointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference sohReportEntry, + TNC_UInt32 sohRELength); +typedef TNC_Result (*TNC_TNCC_SendMessageLongPointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_UInt32 messageFlags, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_VendorID messageVendorID, + TNC_MessageSubtype messageSubtype, + TNC_UInt32 destinationIMVID); typedef TNC_Result (*TNC_TNCC_RequestHandshakeRetryPointer)( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_RetryReason reason); +typedef TNC_Result (*TNC_TNCC_GetAttributePointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_AttributeID attributeID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer, + TNC_UInt32 *pOutValueLength); +typedef TNC_Result (*TNC_TNCC_SetAttributePointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_AttributeID attributeID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer); +typedef TNC_Result (*TNC_TNCC_ReserveAdditionalIMCIDPointer)( + TNC_IMCID imcID, + TNC_UInt32 *pOutIMCID); typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( TNC_IMCID imcID, char *functionName, @@ -104,6 +165,8 @@ typedef TNC_Result (*TNC_IMC_ProvideBindFunctionPointer)( TNC_IMCID imcID, TNC_TNCC_BindFunctionPointer bindFunction); +/* Version Numbers */ + #define TNC_IFIMC_VERSION_1 1 /* Handshake Retry Reason Values */ @@ -118,38 +181,62 @@ typedef TNC_Result (*TNC_IMC_ProvideBindFunctionPointer)( /* reserved for TNC_RETRY_REASON_IMV_MINOR_EVENT: 7 */ /* reserved for TNC_RETRY_REASON_IMV_PERIODIC: 8 */ +/* Message Attribute ID Values */ + +#define TNC_ATTRIBUTEID_SOHR ((TNC_AttributeID) 0x00559708) +#define TNC_ATTRIBUTEID_SSOHR ((TNC_AttributeID) 0x00559709) +#define TNC_ATTRIBUTEID_PRIMARY_IMC_ID ((TNC_AttributeID) 0x00559711) + /* IMC Functions */ -TNC_Result TNC_IMC_Initialize( +TNC_IMC_API TNC_Result TNC_IMC_Initialize( /*in*/ TNC_IMCID imcID, /*in*/ TNC_Version minVersion, /*in*/ TNC_Version maxVersion, /*out*/ TNC_Version *pOutActualVersion); -TNC_Result TNC_IMC_NotifyConnectionChange( +TNC_IMC_API TNC_Result TNC_IMC_NotifyConnectionChange( /*in*/ TNC_IMCID imcID, /*in*/ TNC_ConnectionID connectionID, /*in*/ TNC_ConnectionState newState); -TNC_Result TNC_IMC_BeginHandshake( +TNC_IMC_API TNC_Result TNC_IMC_BeginHandshake( /*in*/ TNC_IMCID imcID, /*in*/ TNC_ConnectionID connectionID); -TNC_Result TNC_IMC_ReceiveMessage( +TNC_IMC_API TNC_Result TNC_IMC_ReceiveMessage( /*in*/ TNC_IMCID imcID, /*in*/ TNC_ConnectionID connectionID, /*in*/ TNC_BufferReference messageBuffer, /*in*/ TNC_UInt32 messageLength, /*in*/ TNC_MessageType messageType); -TNC_Result TNC_IMC_BatchEnding( +TNC_IMC_API TNC_Result TNC_IMC_ReceiveMessageSOH( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_BufferReference sohrReportEntry, +/*in*/ TNC_UInt32 sohrRELength, +/*in*/ TNC_MessageType systemHealthID); + +TNC_IMC_API TNC_Result TNC_IMC_ReceiveMessageLong( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_UInt32 messageFlags, +/*in*/ TNC_BufferReference message, +/*in*/ TNC_UInt32 messageLength, +/*in*/ TNC_VendorID messageVendorID, +/*in*/ TNC_MessageSubtype messageSubtype, +/*in*/ TNC_UInt32 sourceIMVID, +/*in*/ TNC_UInt32 destinationIMCID); + +TNC_IMC_API TNC_Result TNC_IMC_BatchEnding( /*in*/ TNC_IMCID imcID, /*in*/ TNC_ConnectionID connectionID); -TNC_Result TNC_IMC_Terminate( +TNC_IMC_API TNC_Result TNC_IMC_Terminate( /*in*/ TNC_IMCID imcID); -TNC_Result TNC_IMC_ProvideBindFunction( +TNC_IMC_API TNC_Result TNC_IMC_ProvideBindFunction( /*in*/ TNC_IMCID imcID, /*in*/ TNC_TNCC_BindFunctionPointer bindFunction); @@ -160,6 +247,12 @@ TNC_Result TNC_TNCC_ReportMessageTypes( /*in*/ TNC_MessageTypeList supportedTypes, /*in*/ TNC_UInt32 typeCount); +TNC_Result TNC_TNCC_ReportMessageTypesLong( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_VendorIDList supportedVendorIDs, +/*in*/ TNC_MessageSubtypeList supportedSubtypes, +/*in*/ TNC_UInt32 typeCount); + TNC_Result TNC_TNCC_SendMessage( /*in*/ TNC_IMCID imcID, /*in*/ TNC_ConnectionID connectionID, @@ -167,11 +260,45 @@ TNC_Result TNC_TNCC_SendMessage( /*in*/ TNC_UInt32 messageLength, /*in*/ TNC_MessageType messageType); +TNC_Result TNC_TNCC_SendMessageSOH( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_BufferReference sohReportEntry, +/*in*/ TNC_UInt32 sohRELength); + +TNC_Result TNC_TNCC_SendMessageLong( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_UInt32 messageFlags, +/*in*/ TNC_BufferReference message, +/*in*/ TNC_UInt32 messageLength, +/*in*/ TNC_VendorID messageVendorID, +/*in*/ TNC_MessageSubtype messageSubtype, +/*in*/ TNC_UInt32 destinationIMVID); TNC_Result TNC_TNCC_RequestHandshakeRetry( /*in*/ TNC_IMCID imcID, /*in*/ TNC_ConnectionID connectionID, /*in*/ TNC_RetryReason reason); +TNC_Result TNC_TNCC_GetAttribute( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_AttributeID attributeID, +/*in*/ TNC_UInt32 bufferLength, +/*out*/ TNC_BufferReference buffer, +/*out*/ TNC_UInt32 *pOutValueLength); + +TNC_Result TNC_TNCC_SetAttribute( +/*in*/ TNC_IMCID imcID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_AttributeID attributeID, +/*in*/ TNC_UInt32 bufferLength, +/*in*/ TNC_BufferReference buffer); + +TNC_Result TNC_TNCS_ReserveAdditionalIMCID( +/*in*/ TNC_IMCID imcID, +/*out*/ TNC_UInt32 *pOutIMCID); + TNC_Result TNC_TNCC_BindFunction( /*in*/ TNC_IMCID imcID, /*in*/ char *functionName, diff --git a/src/libtncif/tncifimv.h b/src/libtncif/tncifimv.h index 7a2394c06..3c9db0055 100644 --- a/src/libtncif/tncifimv.h +++ b/src/libtncif/tncifimv.h @@ -1,22 +1,22 @@ /* tncifimv.h * - * Trusted Network Connect IF-IMV API version 1.20 + * Trusted Network Connect IF-IMV API version 1.30 * Microsoft Windows DLL Platform Binding C Header - * February 5, 2007 + * October 14, 2011 * - * Copyright(c) 2005-2007, Trusted Computing Group, Inc. All rights + * Copyright(c) 2005-2011, Trusted Computing Group, Inc. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * - Redistributions of source code must retain the above copyright + * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright + * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. - * - Neither the name of the Trusted Computing Group nor the names of + * o Neither the name of the Trusted Computing Group nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. @@ -42,80 +42,134 @@ * respective owners. */ +/** + * @defgroup tncifimv tncifimv + * @{ @ingroup libtncif + */ + #ifndef TNCIFIMV_H_ #define TNCIFIMV_H_ #include "tncif.h" +#ifdef WIN32 +#ifdef TNC_IMV_EXPORTS +#define TNC_IMV_API __declspec(dllexport) +#else +#define TNC_IMV_API __declspec(dllimport) +#endif +#else +#define TNC_IMV_API +#endif + +/* Derived Types */ + typedef TNC_UInt32 TNC_IMVID; typedef TNC_UInt32 TNC_IMV_Action_Recommendation; typedef TNC_UInt32 TNC_IMV_Evaluation_Result; -typedef TNC_UInt32 TNC_AttributeID; /* Function pointers */ typedef TNC_Result (*TNC_IMV_InitializePointer)( - TNC_IMVID imvID, - TNC_Version minVersion, - TNC_Version maxVersion, - TNC_Version *pOutActualVersion); + TNC_IMVID imvID, + TNC_Version minVersion, + TNC_Version maxVersion, + TNC_Version *pOutActualVersion); typedef TNC_Result (*TNC_IMV_NotifyConnectionChangePointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID, - TNC_ConnectionState newState); + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_ConnectionState newState); typedef TNC_Result (*TNC_IMV_ReceiveMessagePointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID, - TNC_BufferReference message, - TNC_UInt32 messageLength, - TNC_MessageType messageType); + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType); +typedef TNC_Result (*TNC_IMV_ReceiveMessageSOHPointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference sohReportEntry, + TNC_UInt32 sohRELength, + TNC_MessageType systemHealthID); +typedef TNC_Result (*TNC_IMV_ReceiveMessageLongPointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_UInt32 messageFlags, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_VendorID messageVendorID, + TNC_MessageSubtype messageSubtype, + TNC_UInt32 sourceIMCID, + TNC_UInt32 destinationIMVID); typedef TNC_Result (*TNC_IMV_SolicitRecommendationPointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID); + TNC_IMVID imvID, + TNC_ConnectionID connectionID); typedef TNC_Result (*TNC_IMV_BatchEndingPointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID); + TNC_IMVID imvID, + TNC_ConnectionID connectionID); typedef TNC_Result (*TNC_IMV_TerminatePointer)( - TNC_IMVID imvID); + TNC_IMVID imvID); typedef TNC_Result (*TNC_TNCS_ReportMessageTypesPointer)( - TNC_IMVID imvID, - TNC_MessageTypeList supportedTypes, - TNC_UInt32 typeCount); + TNC_IMVID imvID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount); +typedef TNC_Result (*TNC_TNCS_ReportMessageTypesLongPointer)( + TNC_IMVID imvID, + TNC_VendorIDList supportedVendorIDs, + TNC_MessageSubtypeList supportedSubtypes, + TNC_UInt32 typeCount); typedef TNC_Result (*TNC_TNCS_SendMessagePointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID, - TNC_BufferReference message, - TNC_UInt32 messageLength, - TNC_MessageType messageType); + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType); +typedef TNC_Result (*TNC_TNCS_SendMessageSOHPointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference sohrReportEntry, + TNC_UInt32 sohrRELength); +typedef TNC_Result (*TNC_TNCS_SendMessageLongPointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_UInt32 messageFlags, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_VendorID messageVendorID, + TNC_MessageSubtype messageSubtype, + TNC_UInt32 destinationIMCID); typedef TNC_Result (*TNC_TNCS_RequestHandshakeRetryPointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID, - TNC_RetryReason reason); + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason); typedef TNC_Result (*TNC_TNCS_ProvideRecommendationPointer)( - TNC_IMVID imvID, - TNC_ConnectionID connectionID, - TNC_IMV_Action_Recommendation recommendation, - TNC_IMV_Evaluation_Result evaluation); + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_IMV_Action_Recommendation recommendation, + TNC_IMV_Evaluation_Result evaluation); typedef TNC_Result (*TNC_TNCS_GetAttributePointer)( - TNC_IMVID imvID, -TNC_ConnectionID connectionID, -TNC_AttributeID attributeID, - TNC_UInt32 bufferLength, - TNC_BufferReference buffer, - TNC_UInt32 *pOutValueLength); -typedef TNC_Result (*TNC_TNCS_SetAttributePointer)( - TNC_IMVID imvID, + TNC_IMVID imvID, TNC_ConnectionID connectionID, -TNC_AttributeID attributeID, - TNC_UInt32 bufferLength, - TNC_BufferReference buffer); + TNC_AttributeID attributeID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer, + TNC_UInt32 *pOutValueLength); +typedef TNC_Result (*TNC_TNCS_SetAttributePointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_AttributeID attributeID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer); +typedef TNC_Result (*TNC_TNCS_ReserveAdditionalIMVIDPointer)( + TNC_IMVID imvID, + TNC_UInt32 *pOutIMVID); typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)( - TNC_IMVID imvID, - char *functionName, - void **pOutfunctionPointer); + TNC_IMVID imvID, + char *functionName, + void **pOutfunctionPointer); typedef TNC_Result (*TNC_IMV_ProvideBindFunctionPointer)( - TNC_IMVID imvID, - TNC_TNCS_BindFunctionPointer bindFunction); + TNC_IMVID imvID, + TNC_TNCS_BindFunctionPointer bindFunction); /* Version Numbers */ @@ -150,42 +204,62 @@ typedef TNC_Result (*TNC_IMV_ProvideBindFunctionPointer)( /* Message Attribute ID Values */ -#define TNC_ATTRIBUTEID_PREFERRED_LANGUAGE ((TNC_AttributeID) 0x00000001) #define TNC_ATTRIBUTEID_REASON_STRING ((TNC_AttributeID) 0x00000002) #define TNC_ATTRIBUTEID_REASON_LANGUAGE ((TNC_AttributeID) 0x00000003) +#define TNC_ATTRIBUTEID_SOH ((TNC_AttributeID) 0x00559706) +#define TNC_ATTRIBUTEID_SSOH ((TNC_AttributeID) 0x00559707) +#define TNC_ATTRIBUTEID_PRIMARY_IMV_ID ((TNC_AttributeID) 0x00559710) /* IMV Functions */ -TNC_Result TNC_IMV_Initialize( +TNC_IMV_API TNC_Result TNC_IMV_Initialize( /*in*/ TNC_IMVID imvID, /*in*/ TNC_Version minVersion, /*in*/ TNC_Version maxVersion, /*in*/ TNC_Version *pOutActualVersion); -TNC_Result TNC_IMV_NotifyConnectionChange( +TNC_IMV_API TNC_Result TNC_IMV_NotifyConnectionChange( /*in*/ TNC_IMVID imvID, /*in*/ TNC_ConnectionID connectionID, /*in*/ TNC_ConnectionState newState); -TNC_Result TNC_IMV_ReceiveMessage( +TNC_IMV_API TNC_Result TNC_IMV_ReceiveMessage( /*in*/ TNC_IMVID imvID, /*in*/ TNC_ConnectionID connectionID, /*in*/ TNC_BufferReference messageBuffer, /*in*/ TNC_UInt32 messageLength, /*in*/ TNC_MessageType messageType); -TNC_Result TNC_IMV_SolicitRecommendation( +TNC_IMV_API TNC_Result TNC_IMV_ReceiveMessageSOH( +/*in*/ TNC_IMVID imvID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_BufferReference sohReportEntry, +/*in*/ TNC_UInt32 sohRELength, +/*in*/ TNC_MessageType systemHealthID); + +TNC_IMV_API TNC_Result TNC_IMV_ReceiveMessageLong( +/*in*/ TNC_IMVID imvID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_UInt32 messageFlags, +/*in*/ TNC_BufferReference message, +/*in*/ TNC_UInt32 messageLength, +/*in*/ TNC_VendorID messageVendorID, +/*in*/ TNC_MessageSubtype messageSubtype, +/*in*/ TNC_UInt32 sourceIMCID, +/*in*/ TNC_UInt32 destinationIMVID); + +TNC_IMV_API TNC_Result TNC_IMV_SolicitRecommendation( /*in*/ TNC_IMVID imvID, /*in*/ TNC_ConnectionID connectionID); -TNC_Result TNC_IMV_BatchEnding( +TNC_IMV_API TNC_Result TNC_IMV_BatchEnding( /*in*/ TNC_IMVID imvID, /*in*/ TNC_ConnectionID connectionID); -TNC_Result TNC_IMV_Terminate( +TNC_IMV_API TNC_Result TNC_IMV_Terminate( /*in*/ TNC_IMVID imvID); -TNC_Result TNC_IMV_ProvideBindFunction( +TNC_IMV_API TNC_Result TNC_IMV_ProvideBindFunction( /*in*/ TNC_IMVID imvID, /*in*/ TNC_TNCS_BindFunctionPointer bindFunction); @@ -196,6 +270,12 @@ TNC_Result TNC_TNCS_ReportMessageTypes( /*in*/ TNC_MessageTypeList supportedTypes, /*in*/ TNC_UInt32 typeCount); +TNC_Result TNC_TNCS_ReportMessageTypesLong( +/*in*/ TNC_IMVID imvID, +/*in*/ TNC_VendorIDList supportedVendorIDs, +/*in*/ TNC_MessageSubtypeList supportedSubtypes, +/*in*/ TNC_UInt32 typeCount); + TNC_Result TNC_TNCS_SendMessage( /*in*/ TNC_IMVID imvID, /*in*/ TNC_ConnectionID connectionID, @@ -203,6 +283,22 @@ TNC_Result TNC_TNCS_SendMessage( /*in*/ TNC_UInt32 messageLength, /*in*/ TNC_MessageType messageType); +TNC_Result TNC_TNCS_SendMessageSOH( +/*in*/ TNC_IMVID imvID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_BufferReference sohrReportEntry, +/*in*/ TNC_UInt32 sohrRELength); + +TNC_Result TNC_TNCS_SendMessageLong( +/*in*/ TNC_IMVID imvID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_UInt32 messageFlags, +/*in*/ TNC_BufferReference message, +/*in*/ TNC_UInt32 messageLength, +/*in*/ TNC_VendorID messageVendorID, +/*in*/ TNC_MessageSubtype messageSubtype, +/*in*/ TNC_UInt32 destinationIMCID); + TNC_Result TNC_TNCS_RequestHandshakeRetry( /*in*/ TNC_IMVID imvID, /*in*/ TNC_ConnectionID connectionID, @@ -222,13 +318,16 @@ TNC_Result TNC_TNCS_GetAttribute( /*out*/ TNC_BufferReference buffer, /*out*/ TNC_UInt32 *pOutValueLength); -TNC_Result TNC_TNCS_SetAttribute( +TNC_Result TNC_TNCS_ReserveAdditionalIMVID( /*in*/ TNC_IMVID imvID, -/*in*/ TNC_ConnectionID connectionID, -/*in*/ TNC_AttributeID attributeID, -/*in*/ TNC_UInt32 bufferLength, -/*in*/ TNC_BufferReference buffer); +/*out*/ TNC_UInt32 *pOutIMVID); +TNC_Result TNC_TNCS_SetAttribute( +/*in*/ TNC_IMVID imvID, +/*in*/ TNC_ConnectionID connectionID, +/*in*/ TNC_AttributeID attributeID, +/*in*/ TNC_UInt32 bufferLength, +/*in*/ TNC_BufferReference buffer); TNC_Result TNC_TNCS_BindFunction( /*in*/ TNC_IMVID imvID, /*in*/ char *functionName, diff --git a/src/pki/command.c b/src/pki/command.c index 0142b4ab7..07ba5bb1d 100644 --- a/src/pki/command.c +++ b/src/pki/command.c @@ -176,6 +176,13 @@ int command_usage(char *error) fprintf(out, "Error: %s\n", error); } fprintf(out, "strongSwan %s PKI tool\n", VERSION); + + if (active == help_idx) + { + fprintf(out, "loaded plugins: %s\n", + lib->plugins->loaded_plugins(lib->plugins)); + } + fprintf(out, "usage:\n"); if (active == help_idx) { diff --git a/src/pki/commands/issue.c b/src/pki/commands/issue.c index 97769fca6..0398c9dc9 100644 --- a/src/pki/commands/issue.c +++ b/src/pki/commands/issue.c @@ -67,11 +67,11 @@ static int issue() char *error = NULL, *keyid = NULL; identification_t *id = NULL; linked_list_t *san, *cdps, *ocsp, *permitted, *excluded, *policies, *mappings; - int lifetime = 1095; int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT; int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT; chunk_t serial = chunk_empty; chunk_t encoding = chunk_empty; + time_t lifetime = 1095; time_t not_before, not_after; x509_flag_t flags = 0; x509_t *x509; diff --git a/src/pki/commands/self.c b/src/pki/commands/self.c index 7852d8594..6813c98f7 100644 --- a/src/pki/commands/self.c +++ b/src/pki/commands/self.c @@ -55,11 +55,11 @@ static int self() char *file = NULL, *dn = NULL, *hex = NULL, *error = NULL, *keyid = NULL; identification_t *id = NULL; linked_list_t *san, *ocsp, *permitted, *excluded, *policies, *mappings; - int lifetime = 1095; int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT; int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT; chunk_t serial = chunk_empty; chunk_t encoding = chunk_empty; + time_t lifetime = 1095; time_t not_before, not_after; x509_flag_t flags = 0; x509_cert_policy_t *policy = NULL; diff --git a/src/pki/commands/signcrl.c b/src/pki/commands/signcrl.c index 9a21bd99c..827fd7318 100644 --- a/src/pki/commands/signcrl.c +++ b/src/pki/commands/signcrl.c @@ -124,7 +124,7 @@ static int sign_crl() int serial_len = 0; crl_reason_t reason = CRL_REASON_UNSPECIFIED; time_t thisUpdate, nextUpdate, date = time(NULL); - int lifetime = 15; + time_t lifetime = 15; linked_list_t *list, *cdps; enumerator_t *enumerator, *lastenum = NULL; x509_cdp_t *cdp; diff --git a/src/pluto/Android.mk b/src/pluto/Android.mk index 4c4bdca92..618f79c42 100644 --- a/src/pluto/Android.mk +++ b/src/pluto/Android.mk @@ -69,6 +69,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) \ LOCAL_MODULE := pluto +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/pluto/Makefile.am b/src/pluto/Makefile.am index 68ba16346..3fd0e039c 100644 --- a/src/pluto/Makefile.am +++ b/src/pluto/Makefile.am @@ -102,6 +102,8 @@ endif dist_man_MANS = pluto.8 +EXTRA_DIST = Android.mk + # compile options ################# diff --git a/src/pluto/ca.c b/src/pluto/ca.c index 175c0b022..827b98121 100644 --- a/src/pluto/ca.c +++ b/src/pluto/ca.c @@ -219,7 +219,8 @@ cert_t* get_authcert(identification_t *subject, chunk_t keyid, } /* compare the subjectDistinguishedNames */ - if (!certificate->has_subject(certificate, subject)) + if (!(subject && certificate->has_subject(certificate, subject)) && + (subject || !keyid.ptr)) { continue; } diff --git a/src/pluto/defs.c b/src/pluto/defs.c index f83318e12..7f3a819de 100644 --- a/src/pluto/defs.c +++ b/src/pluto/defs.c @@ -16,6 +16,7 @@ #include <string.h> #include <stdio.h> #include <dirent.h> +#include <inttypes.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> @@ -91,8 +92,7 @@ mv_chunk(u_char **pos, chunk_t content) const char* check_expiry(time_t expiration_date, int warning_interval, bool strict) { - time_t now; - int time_left; + time_t now, time_left; if (expiration_date == UNDEFINED_TIME) return "ok (expires never)"; @@ -125,8 +125,8 @@ check_expiry(time_t expiration_date, int warning_interval, bool strict) time_left /= 60; unit = "minute"; } - snprintf(buf, 35, "warning (expires in %d %s%s)", time_left, - unit, (time_left == 1)?"":"s"); + snprintf(buf, 35, "warning (expires in %" PRIu64 " %s%s)", + (u_int64_t)time_left, unit, (time_left == 1) ? "" : "s"); return buf; } } diff --git a/src/pluto/keys.c b/src/pluto/keys.c index 9031fcda5..fb61bef5c 100644 --- a/src/pluto/keys.c +++ b/src/pluto/keys.c @@ -835,14 +835,7 @@ static void process_secret(secret_t *s, int whackfd) err_t ugh = NULL; s->kind = SECRET_PSK; /* default */ - if (*tok == '"' || *tok == '\'') - { - log_psk("PSK", s); - - /* old PSK format: just a string */ - ugh = process_psk_secret(&s->u.preshared_secret); - } - else if (tokeqword("psk")) + if (tokeqword("psk")) { log_psk("PSK", s); @@ -989,13 +982,7 @@ static void process_secret_records(int whackfd) for (;;) { - if (tok[0] == '"' || tok[0] == '\'') - { - /* found key part */ - process_secret(s, whackfd); - break; - } - else if (tokeq(":")) + if (tokeq(":")) { /* found key part */ shift(); /* discard explicit separator */ diff --git a/src/pluto/log.c b/src/pluto/log.c index 0bfc8fa9e..f6fa226d5 100644 --- a/src/pluto/log.c +++ b/src/pluto/log.c @@ -868,19 +868,8 @@ DBG_dump(const char *label, const void *p, size_t len) static void show_loaded_plugins() { - char buf[BUF_LEN]; - plugin_t *plugin; - int len = 0; - enumerator_t *enumerator; - - buf[0] = '\0'; - enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); - while (len < BUF_LEN && enumerator->enumerate(enumerator, &plugin, NULL)) - { - len += snprintf(&buf[len], BUF_LEN-len, "%s ", plugin->get_name(plugin)); - } - enumerator->destroy(enumerator); - whack_log(RC_COMMENT, "loaded plugins: %s", buf); + whack_log(RC_COMMENT, "loaded plugins: %s", + lib->plugins->loaded_plugins(lib->plugins)); } void show_status(bool all, const char *name) diff --git a/src/pluto/plutomain.c b/src/pluto/plutomain.c index db5f2d941..dbc857ce2 100644 --- a/src/pluto/plutomain.c +++ b/src/pluto/plutomain.c @@ -264,26 +264,6 @@ static const char *pkcs11_init_args = NULL; /* options read by optionsfrom */ options_t *options; -/** - * Log loaded plugins - */ -static void print_plugins() -{ - char buf[BUF_LEN]; - plugin_t *plugin; - int len = 0; - enumerator_t *enumerator; - - buf[0] = '\0'; - enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); - while (len < BUF_LEN && enumerator->enumerate(enumerator, &plugin, NULL)) - { - len += snprintf(&buf[len], BUF_LEN-len, "%s ", plugin->get_name(plugin)); - } - enumerator->destroy(enumerator); - DBG1(DBG_DMN, "loaded plugins: %s", buf); -} - int main(int argc, char **argv) { bool fork_desired = TRUE; @@ -675,6 +655,9 @@ int main(int argc, char **argv) close(fd); } + /* for uncritical pseudo random numbers */ + srand(time(NULL) + getpid()); + init_constants(); init_log("pluto"); @@ -698,7 +681,8 @@ int main(int argc, char **argv) { exit(SS_RC_INITIALIZATION_FAILED); } - print_plugins(); + DBG1(DBG_DMN, "loaded plugins: %s", + lib->plugins->loaded_plugins(lib->plugins)); init_builder(); if (!init_secret() || !init_crypto()) @@ -852,6 +836,7 @@ void exit_pluto(int status) delete_lock(); options->destroy(options); pluto_deinit(); + lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); lib->plugins->unload(lib->plugins); libhydra_deinit(); library_deinit(); diff --git a/src/scepclient/scepclient.c b/src/scepclient/scepclient.c index 2d364d654..0b54eeee3 100644 --- a/src/scepclient/scepclient.c +++ b/src/scepclient/scepclient.c @@ -276,25 +276,6 @@ usage(const char *message) } /** - * Log loaded plugins - */ -static void print_plugins() -{ - char buf[BUF_LEN]; - plugin_t *plugin; - int len = 0; - enumerator_t *enumerator; - - enumerator = lib->plugins->create_plugin_enumerator(lib->plugins); - while (len < BUF_LEN && enumerator->enumerate(enumerator, &plugin, NULL)) - { - len += snprintf(&buf[len], BUF_LEN-len, "%s ", plugin->get_name(plugin)); - } - enumerator->destroy(enumerator); - DBG1(DBG_LIB, " loaded plugins: %s", buf); -} - -/** * @brief main of scepclient * * @param argc number of arguments @@ -704,10 +685,6 @@ int main(int argc, char **argv) case 'x': /* --maxpolltime */ max_poll_time = atoi(optarg); - if (max_poll_time < 0) - { - usage("invalid maxpolltime specified"); - } continue; case 'a': /*--algorithm */ @@ -763,7 +740,8 @@ int main(int argc, char **argv) { exit_scepclient("plugin loading failed"); } - print_plugins(); + DBG1(DBG_LIB, " loaded plugins: %s", + lib->plugins->loaded_plugins(lib->plugins)); if ((filetype_out == 0) && (!request_ca_certificate)) { diff --git a/src/starter/Android.mk b/src/starter/Android.mk index 48e4b0b27..a82fe9385 100644 --- a/src/starter/Android.mk +++ b/src/starter/Android.mk @@ -30,10 +30,17 @@ endif LOCAL_MODULE := starter +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false +LOCAL_REQUIRED_MODULES := stroke +ifneq ($(strongswan_BUILD_PLUTO),) +LOCAL_REQUIRED_MODULES += whack +endif + LOCAL_SHARED_LIBRARIES += libstrongswan libhydra libfreeswan include $(BUILD_EXECUTABLE) diff --git a/src/starter/Makefile.am b/src/starter/Makefile.am index ba97c060f..94ddf5aba 100644 --- a/src/starter/Makefile.am +++ b/src/starter/Makefile.am @@ -28,7 +28,7 @@ AM_CFLAGS = \ AM_YFLAGS = -v -d starter_LDADD = defs.o $(top_builddir)/src/libfreeswan/libfreeswan.a $(top_builddir)/src/libstrongswan/libstrongswan.la $(top_builddir)/src/libhydra/libhydra.la $(SOCKLIB) -EXTRA_DIST = keywords.txt ipsec.conf +EXTRA_DIST = keywords.txt ipsec.conf Android.mk MAINTAINERCLEANFILES = keywords.c BUILT_SOURCES = parser.h diff --git a/src/starter/confread.c b/src/starter/confread.c index ce69fd724..2fb329c85 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -135,7 +135,7 @@ static void load_setup(starter_config_t *cfg, config_parsed_t *cfgp) kw_token_t token = kw->entry->token; - if (token < KW_SETUP_FIRST || token > KW_SETUP_LAST) + if ((int)token < KW_SETUP_FIRST || token > KW_SETUP_LAST) { plog("# unsupported keyword '%s' in config setup", kw->entry->name); cfg->err++; diff --git a/src/starter/confread.h b/src/starter/confread.h index 19c404e2e..655c97084 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -198,12 +198,12 @@ struct starter_config { char *plutostderrlog; bool uniqueids; u_int overridemtu; - u_int crlcheckinterval; + time_t crlcheckinterval; bool cachecrls; strict_t strictcrlpolicy; bool nocrsend; bool nat_traversal; - u_int keep_alive; + time_t keep_alive; u_int force_keepalive; char *virtual_private; char *pkcs11module; diff --git a/src/starter/invokepluto.c b/src/starter/invokepluto.c index 11c13abe2..70c0692ea 100644 --- a/src/starter/invokepluto.c +++ b/src/starter/invokepluto.c @@ -184,7 +184,7 @@ starter_start_pluto (starter_config_t *cfg, bool no_fork, bool attach_gdb) static char buf1[15]; arg[argc++] = "--crlcheckinterval"; - snprintf(buf1, sizeof(buf1), "%u", cfg->setup.crlcheckinterval); + snprintf(buf1, sizeof(buf1), "%d", (int)cfg->setup.crlcheckinterval); arg[argc++] = buf1; } if (cfg->setup.cachecrls) @@ -212,7 +212,7 @@ starter_start_pluto (starter_config_t *cfg, bool no_fork, bool attach_gdb) static char buf2[15]; arg[argc++] = "--keep_alive"; - snprintf(buf2, sizeof(buf2), "%u", cfg->setup.keep_alive); + snprintf(buf2, sizeof(buf2), "%d", (int)cfg->setup.keep_alive); arg[argc++] = buf2; } if (cfg->setup.virtual_private) diff --git a/src/starter/y.output b/src/starter/y.output new file mode 100644 index 000000000..702cb1f9c --- /dev/null +++ b/src/starter/y.output @@ -0,0 +1,351 @@ +Grammar + + 0 $accept: config_file $end + + 1 config_file: config_file section_or_include + 2 | /* empty */ + + 3 section_or_include: FILE_VERSION STRING EOL + + 4 $@1: /* empty */ + + 5 section_or_include: CONFIG SETUP EOL $@1 kw_section + + 6 $@2: /* empty */ + + 7 section_or_include: CONN STRING EOL $@2 kw_section + + 8 $@3: /* empty */ + + 9 section_or_include: CA STRING EOL $@3 kw_section + + 10 $@4: /* empty */ + + 11 section_or_include: INCLUDE STRING $@4 EOL + 12 | EOL + + 13 kw_section: FIRST_SPACES statement_kw EOL kw_section + 14 | /* empty */ + + 15 statement_kw: STRING EQUAL STRING + 16 | STRING EQUAL + 17 | /* empty */ + + +Terminals, with rules where they appear + +$end (0) 0 +error (256) +EQUAL (258) 15 16 +FIRST_SPACES (259) 13 +EOL (260) 3 5 7 9 11 12 13 +CONFIG (261) 5 +SETUP (262) 5 +CONN (263) 7 +CA (264) 9 +INCLUDE (265) 11 +FILE_VERSION (266) 3 +STRING (267) 3 7 9 11 15 16 + + +Nonterminals, with rules where they appear + +$accept (13) + on left: 0 +config_file (14) + on left: 1 2, on right: 0 1 +section_or_include (15) + on left: 3 5 7 9 11 12, on right: 1 +$@1 (16) + on left: 4, on right: 5 +$@2 (17) + on left: 6, on right: 7 +$@3 (18) + on left: 8, on right: 9 +$@4 (19) + on left: 10, on right: 11 +kw_section (20) + on left: 13 14, on right: 5 7 9 13 +statement_kw (21) + on left: 15 16 17, on right: 13 + + +state 0 + + 0 $accept: . config_file $end + + $default reduce using rule 2 (config_file) + + config_file go to state 1 + + +state 1 + + 0 $accept: config_file . $end + 1 config_file: config_file . section_or_include + + $end shift, and go to state 2 + EOL shift, and go to state 3 + CONFIG shift, and go to state 4 + CONN shift, and go to state 5 + CA shift, and go to state 6 + INCLUDE shift, and go to state 7 + FILE_VERSION shift, and go to state 8 + + section_or_include go to state 9 + + +state 2 + + 0 $accept: config_file $end . + + $default accept + + +state 3 + + 12 section_or_include: EOL . + + $default reduce using rule 12 (section_or_include) + + +state 4 + + 5 section_or_include: CONFIG . SETUP EOL $@1 kw_section + + SETUP shift, and go to state 10 + + +state 5 + + 7 section_or_include: CONN . STRING EOL $@2 kw_section + + STRING shift, and go to state 11 + + +state 6 + + 9 section_or_include: CA . STRING EOL $@3 kw_section + + STRING shift, and go to state 12 + + +state 7 + + 11 section_or_include: INCLUDE . STRING $@4 EOL + + STRING shift, and go to state 13 + + +state 8 + + 3 section_or_include: FILE_VERSION . STRING EOL + + STRING shift, and go to state 14 + + +state 9 + + 1 config_file: config_file section_or_include . + + $default reduce using rule 1 (config_file) + + +state 10 + + 5 section_or_include: CONFIG SETUP . EOL $@1 kw_section + + EOL shift, and go to state 15 + + +state 11 + + 7 section_or_include: CONN STRING . EOL $@2 kw_section + + EOL shift, and go to state 16 + + +state 12 + + 9 section_or_include: CA STRING . EOL $@3 kw_section + + EOL shift, and go to state 17 + + +state 13 + + 11 section_or_include: INCLUDE STRING . $@4 EOL + + $default reduce using rule 10 ($@4) + + $@4 go to state 18 + + +state 14 + + 3 section_or_include: FILE_VERSION STRING . EOL + + EOL shift, and go to state 19 + + +state 15 + + 5 section_or_include: CONFIG SETUP EOL . $@1 kw_section + + $default reduce using rule 4 ($@1) + + $@1 go to state 20 + + +state 16 + + 7 section_or_include: CONN STRING EOL . $@2 kw_section + + $default reduce using rule 6 ($@2) + + $@2 go to state 21 + + +state 17 + + 9 section_or_include: CA STRING EOL . $@3 kw_section + + $default reduce using rule 8 ($@3) + + $@3 go to state 22 + + +state 18 + + 11 section_or_include: INCLUDE STRING $@4 . EOL + + EOL shift, and go to state 23 + + +state 19 + + 3 section_or_include: FILE_VERSION STRING EOL . + + $default reduce using rule 3 (section_or_include) + + +state 20 + + 5 section_or_include: CONFIG SETUP EOL $@1 . kw_section + + FIRST_SPACES shift, and go to state 24 + + $default reduce using rule 14 (kw_section) + + kw_section go to state 25 + + +state 21 + + 7 section_or_include: CONN STRING EOL $@2 . kw_section + + FIRST_SPACES shift, and go to state 24 + + $default reduce using rule 14 (kw_section) + + kw_section go to state 26 + + +state 22 + + 9 section_or_include: CA STRING EOL $@3 . kw_section + + FIRST_SPACES shift, and go to state 24 + + $default reduce using rule 14 (kw_section) + + kw_section go to state 27 + + +state 23 + + 11 section_or_include: INCLUDE STRING $@4 EOL . + + $default reduce using rule 11 (section_or_include) + + +state 24 + + 13 kw_section: FIRST_SPACES . statement_kw EOL kw_section + + STRING shift, and go to state 28 + + $default reduce using rule 17 (statement_kw) + + statement_kw go to state 29 + + +state 25 + + 5 section_or_include: CONFIG SETUP EOL $@1 kw_section . + + $default reduce using rule 5 (section_or_include) + + +state 26 + + 7 section_or_include: CONN STRING EOL $@2 kw_section . + + $default reduce using rule 7 (section_or_include) + + +state 27 + + 9 section_or_include: CA STRING EOL $@3 kw_section . + + $default reduce using rule 9 (section_or_include) + + +state 28 + + 15 statement_kw: STRING . EQUAL STRING + 16 | STRING . EQUAL + + EQUAL shift, and go to state 30 + + +state 29 + + 13 kw_section: FIRST_SPACES statement_kw . EOL kw_section + + EOL shift, and go to state 31 + + +state 30 + + 15 statement_kw: STRING EQUAL . STRING + 16 | STRING EQUAL . + + STRING shift, and go to state 32 + + $default reduce using rule 16 (statement_kw) + + +state 31 + + 13 kw_section: FIRST_SPACES statement_kw EOL . kw_section + + FIRST_SPACES shift, and go to state 24 + + $default reduce using rule 14 (kw_section) + + kw_section go to state 33 + + +state 32 + + 15 statement_kw: STRING EQUAL STRING . + + $default reduce using rule 15 (statement_kw) + + +state 33 + + 13 kw_section: FIRST_SPACES statement_kw EOL kw_section . + + $default reduce using rule 13 (kw_section) diff --git a/src/stroke/Android.mk b/src/stroke/Android.mk index 833130e25..69b3e54ca 100644 --- a/src/stroke/Android.mk +++ b/src/stroke/Android.mk @@ -15,6 +15,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := stroke +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/stroke/Makefile.am b/src/stroke/Makefile.am index 6eee8cd6c..f93680b64 100644 --- a/src/stroke/Makefile.am +++ b/src/stroke/Makefile.am @@ -5,7 +5,7 @@ stroke.c stroke_msg.h stroke_keywords.c stroke_keywords.h stroke_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la $(SOCKLIB) INCLUDES = -I$(top_srcdir)/src/libstrongswan -EXTRA_DIST = stroke_keywords.txt +EXTRA_DIST = stroke_keywords.txt Android.mk BUILT_SOURCES = stroke_keywords.c MAINTAINERCLEANFILES = stroke_keywords.c AM_CFLAGS = -DIPSEC_PIDDIR=\"${piddir}\" diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index e70245362..6aadd3ec9 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -392,7 +392,7 @@ static void exit_usage(char *error) printf(" where: START and optional END define the clients source IP\n"); printf(" Set loglevel for a logging type:\n"); printf(" stroke loglevel TYPE LEVEL\n"); - printf(" where: TYPE is any|dmn|mgr|ike|chd|job|cfg|knl|net|enc|tnc|imc|imv|pts|tls|lib\n"); + printf(" where: TYPE is any|dmn|mgr|ike|chd|job|cfg|knl|net|asn|enc|tnc|imc|imv|pts|tls|lib\n"); printf(" LEVEL is -1|0|1|2|3|4\n"); printf(" Show connection status:\n"); printf(" stroke status\n"); diff --git a/src/whack/Android.mk b/src/whack/Android.mk index 08d96a071..bf5ec0e98 100644 --- a/src/whack/Android.mk +++ b/src/whack/Android.mk @@ -18,6 +18,8 @@ LOCAL_CFLAGS := $(strongswan_CFLAGS) LOCAL_MODULE := whack +LOCAL_MODULE_TAGS := optional + LOCAL_ARM_MODE := arm LOCAL_PRELINK_MODULE := false diff --git a/src/whack/Makefile.am b/src/whack/Makefile.am index fa4c9959c..23374475e 100644 --- a/src/whack/Makefile.am +++ b/src/whack/Makefile.am @@ -15,3 +15,4 @@ $(top_builddir)/src/libfreeswan/libfreeswan.a AM_CFLAGS = -DDEBUG -DIPSEC_PIDDIR=\"${piddir}\" +EXTRA_DIST = Android.mk |