diff options
Diffstat (limited to 'src/libtls')
-rw-r--r-- | src/libtls/Makefile.am | 5 | ||||
-rw-r--r-- | src/libtls/Makefile.in | 97 | ||||
-rw-r--r-- | src/libtls/tls.c | 12 | ||||
-rw-r--r-- | src/libtls/tls.h | 9 | ||||
-rw-r--r-- | src/libtls/tls_alert.h | 2 | ||||
-rw-r--r-- | src/libtls/tls_application.h | 8 | ||||
-rw-r--r-- | src/libtls/tls_cache.c | 237 | ||||
-rw-r--r-- | src/libtls/tls_cache.h | 78 | ||||
-rw-r--r-- | src/libtls/tls_compression.h | 4 | ||||
-rw-r--r-- | src/libtls/tls_crypto.c | 174 | ||||
-rw-r--r-- | src/libtls/tls_crypto.h | 60 | ||||
-rw-r--r-- | src/libtls/tls_fragmentation.c | 50 | ||||
-rw-r--r-- | src/libtls/tls_fragmentation.h | 4 | ||||
-rw-r--r-- | src/libtls/tls_handshake.h | 22 | ||||
-rw-r--r-- | src/libtls/tls_peer.c | 169 | ||||
-rw-r--r-- | src/libtls/tls_protection.c | 57 | ||||
-rw-r--r-- | src/libtls/tls_protection.h | 4 | ||||
-rw-r--r-- | src/libtls/tls_reader.c | 200 | ||||
-rw-r--r-- | src/libtls/tls_reader.h | 131 | ||||
-rw-r--r-- | src/libtls/tls_server.c | 209 | ||||
-rw-r--r-- | src/libtls/tls_socket.c | 115 | ||||
-rw-r--r-- | src/libtls/tls_socket.h | 22 | ||||
-rw-r--r-- | src/libtls/tls_writer.c | 237 | ||||
-rw-r--r-- | src/libtls/tls_writer.h | 136 |
24 files changed, 1012 insertions, 1030 deletions
diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am index a58e783d7..4cc1a1bdb 100644 --- a/src/libtls/Makefile.am +++ b/src/libtls/Makefile.am @@ -1,7 +1,7 @@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -noinst_LTLIBRARIES = libtls.la +ipseclib_LTLIBRARIES = libtls.la libtls_la_SOURCES = \ tls_protection.h tls_protection.c \ tls_compression.h tls_compression.c \ @@ -9,10 +9,9 @@ libtls_la_SOURCES = \ tls_alert.h tls_alert.c \ tls_crypto.h tls_crypto.c \ tls_prf.h tls_prf.c \ - tls_reader.h tls_reader.c \ - tls_writer.h tls_writer.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/Makefile.in b/src/libtls/Makefile.in index 5a1aa81c0..844b65156 100644 --- a/src/libtls/Makefile.in +++ b/src/libtls/Makefile.in @@ -51,12 +51,34 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -LTLIBRARIES = $(noinst_LTLIBRARIES) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(ipseclibdir)" +LTLIBRARIES = $(ipseclib_LTLIBRARIES) libtls_la_LIBADD = am_libtls_la_OBJECTS = tls_protection.lo tls_compression.lo \ tls_fragmentation.lo tls_alert.lo tls_crypto.lo tls_prf.lo \ - tls_reader.lo tls_writer.lo tls_socket.lo tls_eap.lo \ - tls_peer.lo tls_server.lo tls.lo + tls_socket.lo tls_eap.lo tls_cache.lo tls_peer.lo \ + tls_server.lo tls.lo libtls_la_OBJECTS = $(am_libtls_la_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -167,6 +189,9 @@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +attest_plugins = @attest_plugins@ +axis2c_CFLAGS = @axis2c_CFLAGS@ +axis2c_LIBS = @axis2c_LIBS@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -175,6 +200,7 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ @@ -191,11 +217,13 @@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ +imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ +ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ @@ -239,6 +267,7 @@ sharedstatedir = @sharedstatedir@ soup_CFLAGS = @soup_CFLAGS@ soup_LIBS = @soup_LIBS@ srcdir = @srcdir@ +starter_plugins = @starter_plugins@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ @@ -250,7 +279,7 @@ urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -noinst_LTLIBRARIES = libtls.la +ipseclib_LTLIBRARIES = libtls.la libtls_la_SOURCES = \ tls_protection.h tls_protection.c \ tls_compression.h tls_compression.c \ @@ -258,10 +287,9 @@ libtls_la_SOURCES = \ tls_alert.h tls_alert.c \ tls_crypto.h tls_crypto.c \ tls_prf.h tls_prf.c \ - tls_reader.h tls_reader.c \ - tls_writer.h tls_writer.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 @@ -300,17 +328,39 @@ $(top_srcdir)/configure: $(am__configure_deps) $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +install-ipseclibLTLIBRARIES: $(ipseclib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(ipseclibdir)" || $(MKDIR_P) "$(DESTDIR)$(ipseclibdir)" + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(ipseclibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(ipseclibdir)"; \ + } + +uninstall-ipseclibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(ipseclib_LTLIBRARIES)'; test -n "$(ipseclibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(ipseclibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(ipseclibdir)/$$f"; \ + done -clean-noinstLTLIBRARIES: - -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) - @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ +clean-ipseclibLTLIBRARIES: + -test -z "$(ipseclib_LTLIBRARIES)" || rm -f $(ipseclib_LTLIBRARIES) + @list='$(ipseclib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done libtls.la: $(libtls_la_OBJECTS) $(libtls_la_DEPENDENCIES) - $(LINK) $(libtls_la_OBJECTS) $(libtls_la_LIBADD) $(LIBS) + $(LINK) -rpath $(ipseclibdir) $(libtls_la_OBJECTS) $(libtls_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -320,6 +370,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_compression.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypto.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_eap.Plo@am__quote@ @@ -327,10 +378,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_peer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_prf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_protection.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_reader.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_socket.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_writer.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -445,6 +494,9 @@ check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) installdirs: + for dir in "$(DESTDIR)$(ipseclibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done install: install-am install-exec: install-exec-am install-data: install-data-am @@ -472,7 +524,7 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ +clean-am: clean-generic clean-ipseclibLTLIBRARIES clean-libtool \ mostlyclean-am distclean: distclean-am @@ -493,7 +545,7 @@ info: info-am info-am: -install-data-am: +install-data-am: install-ipseclibLTLIBRARIES install-dvi: install-dvi-am @@ -539,22 +591,23 @@ ps: ps-am ps-am: -uninstall-am: +uninstall-am: uninstall-ipseclibLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + clean-ipseclibLTLIBRARIES clean-libtool ctags distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ - pdf pdf-am ps ps-am tags uninstall uninstall-am + install-html-am install-info install-info-am \ + install-ipseclibLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-ipseclibLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/src/libtls/tls.c b/src/libtls/tls.c index ea527b122..2bcaffbc8 100644 --- a/src/libtls/tls.c +++ b/src/libtls/tls.c @@ -192,6 +192,14 @@ struct private_tls_t { size_t headpos; }; +/** + * Described in header. + */ +void libtls_init(void) +{ + /* empty */ +} + METHOD(tls_t, process, status_t, private_tls_t *this, void *buf, size_t buflen) { @@ -429,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; @@ -464,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 54b0621b5..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 @@ -228,6 +229,11 @@ struct tls_t { }; /** + * Dummy libtls initialization function needed for integrity test + */ +void libtls_init(void); + +/** * Create a tls instance. * * @param is_server TRUE to act as server, FALSE for client @@ -235,10 +241,11 @@ struct tls_t { * @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_alert.h b/src/libtls/tls_alert.h index 95ba4d91b..8ce50f83d 100644 --- a/src/libtls/tls_alert.h +++ b/src/libtls/tls_alert.h @@ -98,7 +98,7 @@ struct tls_alert_t { /** * Did a fatal alert occur?. * - * @return TRUE if a fatal alert has occured + * @return TRUE if a fatal alert has occurred */ bool (*fatal)(tls_alert_t *this); diff --git a/src/libtls/tls_application.h b/src/libtls/tls_application.h index b54a25e22..bd839fbb6 100644 --- a/src/libtls/tls_application.h +++ b/src/libtls/tls_application.h @@ -23,8 +23,8 @@ typedef struct tls_application_t tls_application_t; -#include "tls_reader.h" -#include "tls_writer.h" +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> /** * TLS application data interface. @@ -40,7 +40,7 @@ struct tls_application_t { * - FAILED if application data processing failed * - NEED_MORE if another invocation of process/build needed */ - status_t (*process)(tls_application_t *this, tls_reader_t *reader); + status_t (*process)(tls_application_t *this, bio_reader_t *reader); /** * Build TLS application data to send out. @@ -52,7 +52,7 @@ struct tls_application_t { * - NEED_MORE if more data ready for delivery * - INVALID_STATE if more input to process() required */ - status_t (*build)(tls_application_t *this, tls_writer_t *writer); + status_t (*build)(tls_application_t *this, bio_writer_t *writer); /** * Destroy a tls_application_t. 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 b000f9d47..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,13 +602,13 @@ 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 }, }; /** - * Look up algoritms by a suite + * Look up algorithms by a suite */ static suite_algs_t *find_suite(tls_cipher_suite_t suite) { @@ -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; @@ -1057,15 +1062,15 @@ METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t, } METHOD(tls_crypto_t, get_signature_algorithms, void, - private_tls_crypto_t *this, tls_writer_t *writer) + private_tls_crypto_t *this, bio_writer_t *writer) { - tls_writer_t *supported; + bio_writer_t *supported; enumerator_t *enumerator; hash_algorithm_t alg; tls_hash_algorithm_t hash; const char *plugin_name; - supported = tls_writer_create(32); + supported = bio_writer_create(32); enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); while (enumerator->enumerate(enumerator, &alg, &plugin_name)) { @@ -1280,13 +1285,13 @@ static signature_scheme_t hashsig_to_scheme(key_type_t type, } METHOD(tls_crypto_t, sign, bool, - private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer, + private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t data, chunk_t hashsig) { if (this->tls->get_version(this->tls) >= TLS_1_2) { signature_scheme_t scheme; - tls_reader_t *reader; + bio_reader_t *reader; u_int8_t hash, alg; chunk_t sig; bool done = FALSE; @@ -1296,7 +1301,7 @@ METHOD(tls_crypto_t, sign, bool, hashsig = chunk_from_chars( TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA); } - reader = tls_reader_create(hashsig); + reader = bio_reader_create(hashsig); while (reader->remaining(reader) >= 2) { if (reader->read_uint8(reader, &hash) && @@ -1361,7 +1366,7 @@ METHOD(tls_crypto_t, sign, bool, } METHOD(tls_crypto_t, verify, bool, - private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader, + private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader, chunk_t data) { if (this->tls->get_version(this->tls) >= TLS_1_2) @@ -1432,14 +1437,14 @@ METHOD(tls_crypto_t, verify, bool, } METHOD(tls_crypto_t, sign_handshake, bool, - private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer, + private_tls_crypto_t *this, private_key_t *key, bio_writer_t *writer, chunk_t hashsig) { return sign(this, key, writer, this->handshake, hashsig); } METHOD(tls_crypto_t, verify_handshake, bool, - private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader) + private_tls_crypto_t *this, public_key_t *key, bio_reader_t *reader) { return verify(this, key, reader, this->handshake); } @@ -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 f57b8f3e1..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, @@ -427,7 +427,7 @@ struct tls_crypto_t { * * @param writer writer to write supported hash/sig algorithms */ - void (*get_signature_algorithms)(tls_crypto_t *this, tls_writer_t *writer); + void (*get_signature_algorithms)(tls_crypto_t *this, bio_writer_t *writer); /** * Create an enumerator over supported ECDH groups. @@ -464,7 +464,7 @@ struct tls_crypto_t { * @return TRUE if signature create successfully */ bool (*sign)(tls_crypto_t *this, private_key_t *key, - tls_writer_t *writer, chunk_t data, chunk_t hashsig); + bio_writer_t *writer, chunk_t data, chunk_t hashsig); /** * Verify a blob of data, read signature from a reader. @@ -475,7 +475,7 @@ struct tls_crypto_t { * @return TRUE if signature valid */ bool (*verify)(tls_crypto_t *this, public_key_t *key, - tls_reader_t *reader, chunk_t data); + bio_reader_t *reader, chunk_t data); /** * Create a signature of the handshake data using a given private key. @@ -486,7 +486,7 @@ struct tls_crypto_t { * @return TRUE if signature create successfully */ bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, - tls_writer_t *writer, chunk_t hashsig); + bio_writer_t *writer, chunk_t hashsig); /** * Verify the signature over handshake data using a given public key. @@ -496,7 +496,7 @@ struct tls_crypto_t { * @return TRUE if signature valid */ bool (*verify_handshake)(tls_crypto_t *this, public_key_t *key, - tls_reader_t *reader); + bio_reader_t *reader); /** * Calculate the data of a TLS finished message. @@ -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 5a598cfc4..62e36aaec 100644 --- a/src/libtls/tls_fragmentation.c +++ b/src/libtls/tls_fragmentation.c @@ -15,8 +15,7 @@ #include "tls_fragmentation.h" -#include "tls_reader.h" - +#include <bio/bio_reader.h> #include <debug.h> typedef struct private_tls_fragmentation_t private_tls_fragmentation_t; @@ -108,7 +107,7 @@ struct private_tls_fragmentation_t { * Process a TLS alert */ static status_t process_alert(private_tls_fragmentation_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { u_int8_t level, description; @@ -125,11 +124,11 @@ static status_t process_alert(private_tls_fragmentation_t *this, * Process TLS handshake protocol data */ static status_t process_handshake(private_tls_fragmentation_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { while (reader->remaining(reader)) { - tls_reader_t *msg; + bio_reader_t *msg; u_int8_t type; u_int32_t len; status_t status; @@ -178,7 +177,7 @@ static status_t process_handshake(private_tls_fragmentation_t *this, if (this->input.len == this->inpos) { /* message completely defragmented, process */ - msg = tls_reader_create(this->input); + msg = bio_reader_create(this->input); DBG2(DBG_TLS, "received TLS %N handshake (%u bytes)", tls_handshake_type_names, this->type, this->input.len); status = this->handshake->process(this->handshake, this->type, msg); @@ -201,11 +200,12 @@ static status_t process_handshake(private_tls_fragmentation_t *this, * Process TLS application data */ static status_t process_application(private_tls_fragmentation_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { while (reader->remaining(reader)) { status_t status; + chunk_t data; if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN) { @@ -213,6 +213,8 @@ static status_t process_application(private_tls_fragmentation_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } + data = reader->peek(reader); + DBG3(DBG_TLS, "%B", &data); status = this->application->process(this->application, reader); switch (status) { @@ -233,24 +235,25 @@ static status_t process_application(private_tls_fragmentation_t *this, METHOD(tls_fragmentation_t, process, status_t, private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data) { - tls_reader_t *reader; + bio_reader_t *reader; status_t status; switch (this->state) { case ALERT_SENDING: case ALERT_SENT: - /* don't accept more input, fatal error ocurred */ + /* don't accept more input, fatal error occurred */ return NEED_MORE; case ALERT_NONE: break; } - reader = tls_reader_create(data); + reader = bio_reader_create(data); 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; } @@ -281,11 +284,11 @@ static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data) { tls_alert_level_t level; tls_alert_desc_t desc; - tls_writer_t *writer; + bio_writer_t *writer; if (this->alert->get(this->alert, &level, &desc)) { - writer = tls_writer_create(2); + writer = bio_writer_create(2); writer->write_uint8(writer, level); writer->write_uint8(writer, desc); @@ -302,14 +305,14 @@ static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data) */ static status_t build_handshake(private_tls_fragmentation_t *this) { - tls_writer_t *hs, *msg; + bio_writer_t *hs, *msg; tls_handshake_type_t type; status_t status; - msg = tls_writer_create(64); + msg = bio_writer_create(64); while (TRUE) { - hs = tls_writer_create(64); + hs = bio_writer_create(64); status = this->handshake->build(this->handshake, &type, hs); switch (status) { @@ -322,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)); @@ -343,10 +350,10 @@ static status_t build_handshake(private_tls_fragmentation_t *this) */ static status_t build_application(private_tls_fragmentation_t *this) { - tls_writer_t *msg; + bio_writer_t *msg; status_t status; - msg = tls_writer_create(64); + msg = bio_writer_create(64); while (TRUE) { status = this->application->build(this->application, msg); @@ -395,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 6703b341b..bea0024eb 100644 --- a/src/libtls/tls_handshake.h +++ b/src/libtls/tls_handshake.h @@ -24,8 +24,9 @@ typedef struct tls_handshake_t tls_handshake_t; #include "tls.h" -#include "tls_reader.h" -#include "tls_writer.h" + +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> /** * TLS handshake state machine interface. @@ -44,7 +45,7 @@ struct tls_handshake_t { * - DESTROY_ME if a fatal TLS alert received */ status_t (*process)(tls_handshake_t *this, - tls_handshake_type_t type, tls_reader_t *reader); + tls_handshake_type_t type, bio_reader_t *reader); /** * Build TLS handshake messages to send out. @@ -58,21 +59,22 @@ struct tls_handshake_t { * - INVALID_STATE if more input to process() required */ status_t (*build)(tls_handshake_t *this, - tls_handshake_type_t *type, tls_writer_t *writer); + 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 621f1729d..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; @@ -124,12 +134,12 @@ struct private_tls_peer_t { * Process a server hello message */ static status_t process_server_hello(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { 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; } @@ -209,10 +237,10 @@ static bool check_certificate(private_tls_peer_t *this, certificate_t *cert) * Process a Certificate message */ static status_t process_certificate(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { certificate_t *cert; - tls_reader_t *certs; + bio_reader_t *certs; chunk_t data; bool first = TRUE; @@ -225,7 +253,7 @@ static status_t process_certificate(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - certs = tls_reader_create(data); + certs = bio_reader_create(data); while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) @@ -302,7 +330,7 @@ static public_key_t *find_public_key(private_tls_peer_t *this) * Process a Key Exchange message using MODP Diffie Hellman */ static status_t process_modp_key_exchange(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { chunk_t prime, generator, pub, chunk; public_key_t *public; @@ -379,7 +407,7 @@ static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this, * Process a Key Exchange message using EC Diffie Hellman */ static status_t process_ec_key_exchange(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { diffie_hellman_group_t group; public_key_t *public; @@ -466,7 +494,7 @@ static status_t process_ec_key_exchange(private_tls_peer_t *this, * Process a Server Key Exchange */ static status_t process_key_exchange(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { diffie_hellman_group_t group; @@ -491,10 +519,10 @@ static status_t process_key_exchange(private_tls_peer_t *this, /** * Process a Certificate Request message */ -static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) +static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) { chunk_t types, hashsig, data; - tls_reader_t *authorities; + bio_reader_t *authorities; identification_t *id; certificate_t *cert; @@ -529,7 +557,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - authorities = tls_reader_create(data); + authorities = bio_reader_create(data); while (authorities->remaining(authorities)) { if (!authorities->read_data16(authorities, &data)) @@ -565,7 +593,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) * Process Hello Done message */ static status_t process_hello_done(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO_DONE, reader->peek(reader)); @@ -576,7 +604,7 @@ static status_t process_hello_done(private_tls_peer_t *this, /** * Process finished message */ -static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader) +static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) { chunk_t received; char buf[12]; @@ -599,15 +627,14 @@ static status_t process_finished(private_tls_peer_t *this, tls_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; } METHOD(tls_handshake_t, process, status_t, - private_tls_peer_t *this, tls_handshake_type_t type, tls_reader_t *reader) + private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader) { tls_handshake_type_t expected; @@ -670,10 +697,10 @@ METHOD(tls_handshake_t, process, status_t, * Send a client hello */ static status_t send_client_hello(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { tls_cipher_suite_t *suites; - tls_writer_t *extensions, *curves = NULL; + bio_writer_t *extensions, *curves = NULL; tls_version_t version; tls_named_curve_t curve; enumerator_t *enumerator; @@ -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); @@ -711,7 +739,7 @@ static status_t send_client_hello(private_tls_peer_t *this, writer->write_uint8(writer, 1); writer->write_uint8(writer, 0); - extensions = tls_writer_create(32); + extensions = bio_writer_create(32); extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); this->crypto->get_signature_algorithms(this->crypto, extensions); @@ -723,7 +751,7 @@ static status_t send_client_hello(private_tls_peer_t *this, if (!curves) { extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES); - curves = tls_writer_create(16); + curves = bio_writer_create(16); } curves->write_uint16(curves, curve); } @@ -741,11 +769,11 @@ static status_t send_client_hello(private_tls_peer_t *this, } if (this->server->get_type(this->server) == ID_FQDN) { - tls_writer_t *names; + bio_writer_t *names; DBG2(DBG_TLS, "sending Server Name Indication for '%Y'", this->server); - names = tls_writer_create(8); + names = bio_writer_create(8); names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME); names->write_data16(names, this->server->get_encoding(this->server)); names->wrap16(names); @@ -769,7 +797,7 @@ static status_t send_client_hello(private_tls_peer_t *this, static private_key_t *find_private_key(private_tls_peer_t *this) { private_key_t *key = NULL; - tls_reader_t *reader; + bio_reader_t *reader; key_type_t type; u_int8_t cert; @@ -777,7 +805,7 @@ static private_key_t *find_private_key(private_tls_peer_t *this) { return NULL; } - reader = tls_reader_create(this->cert_types); + reader = bio_reader_create(this->cert_types); while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) { switch (cert) @@ -806,12 +834,12 @@ static private_key_t *find_private_key(private_tls_peer_t *this) * Send Certificate */ static status_t send_certificate(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { enumerator_t *enumerator; certificate_t *cert; auth_rule_t rule; - tls_writer_t *certs; + bio_writer_t *certs; chunk_t data; this->private = find_private_key(this); @@ -823,7 +851,7 @@ static status_t send_certificate(private_tls_peer_t *this, } /* generate certificate payload */ - certs = tls_writer_create(256); + certs = bio_writer_create(256); if (this->peer) { cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); @@ -867,7 +895,7 @@ static status_t send_certificate(private_tls_peer_t *this, * Send client key exchange, using premaster encryption */ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { public_key_t *public; rng_t *rng; @@ -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)); @@ -919,7 +948,7 @@ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, * Send client key exchange, using DHE exchange */ static status_t send_key_exchange_dhe(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { chunk_t premaster, pub; @@ -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); @@ -957,7 +987,7 @@ static status_t send_key_exchange_dhe(private_tls_peer_t *this, * Send client key exchange, depending on suite */ static status_t send_key_exchange(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { if (this->dh) { @@ -970,7 +1000,7 @@ static status_t send_key_exchange(private_tls_peer_t *this, * Send certificate verify */ static status_t send_certificate_verify(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { if (!this->private || !this->crypto->sign_handshake(this->crypto, this->private, @@ -991,7 +1021,7 @@ static status_t send_certificate_verify(private_tls_peer_t *this, * Send Finished */ static status_t send_finished(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { char buf[12]; @@ -1011,7 +1041,7 @@ static status_t send_finished(private_tls_peer_t *this, } METHOD(tls_handshake_t, build, status_t, - private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer) + private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { switch (this->state) { @@ -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.c b/src/libtls/tls_protection.c index d823bae04..dc734545c 100644 --- a/src/libtls/tls_protection.c +++ b/src/libtls/tls_protection.c @@ -91,28 +91,33 @@ struct private_tls_protection_t { }; /** - * Create the header to append to the record data to create the MAC + * Create the header and feed it into a signer for MAC verification */ -static chunk_t sigheader(u_int32_t seq, u_int8_t type, - u_int16_t version, u_int16_t length) +static void sigheader(signer_t *signer, u_int32_t seq, u_int8_t type, + u_int16_t version, u_int16_t length) { /* we only support 32 bit sequence numbers, but TLS uses 64 bit */ - u_int32_t seq_high = 0; - - seq = htonl(seq); - version = htons(version); - length = htons(length); - - return chunk_cat("ccccc", chunk_from_thing(seq_high), - chunk_from_thing(seq), chunk_from_thing(type), - chunk_from_thing(version), chunk_from_thing(length)); + struct __attribute__((__packed__)) { + u_int32_t seq_high; + u_int32_t seq_low; + u_int8_t type; + u_int16_t version; + u_int16_t length; + } header = { + .type = type, + }; + htoun32(&header.seq_low, seq); + htoun16(&header.version, version); + htoun16(&header.length, length); + + signer->get_signature(signer, chunk_from_thing(header), NULL); } METHOD(tls_protection_t, process, status_t, private_tls_protection_t *this, tls_content_type_t type, chunk_t data) { if (this->alert->fatal(this->alert)) - { /* don't accept more input, fatal error ocurred */ + { /* don't accept more input, fatal error occurred */ return NEED_MORE; } @@ -154,17 +159,15 @@ METHOD(tls_protection_t, process, status_t, } padding_length = data.ptr[data.len - 1]; - if (padding_length >= data.len) - { - DBG1(DBG_TLS, "invalid TLS record padding"); - this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); - return NEED_MORE; + if (padding_length < data.len) + { /* remove padding if it looks valid. Continue with no padding, try + * to prevent timing attacks. */ + data.len -= padding_length + 1; } - data.len -= padding_length + 1; } if (this->signer_in) { - chunk_t mac, macdata, header; + chunk_t mac; u_int8_t bs; bs = this->signer_in->get_block_size(this->signer_in); @@ -177,16 +180,13 @@ METHOD(tls_protection_t, process, status_t, mac = chunk_skip(data, data.len - bs); data.len -= bs; - header = sigheader(this->seq_in, type, this->version, data.len); - macdata = chunk_cat("mc", header, data); - if (!this->signer_in->verify_signature(this->signer_in, macdata, mac)) + sigheader(this->signer_in, this->seq_in, type, this->version, data.len); + if (!this->signer_in->verify_signature(this->signer_in, data, mac)) { DBG1(DBG_TLS, "TLS record MAC verification failed"); - free(macdata.ptr); this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); return NEED_MORE; } - free(macdata.ptr); } if (type == TLS_CHANGE_CIPHER_SPEC) @@ -216,11 +216,10 @@ METHOD(tls_protection_t, build, status_t, { if (this->signer_out) { - chunk_t mac, header; + chunk_t mac; - header = sigheader(this->seq_out, *type, this->version, data->len); - this->signer_out->get_signature(this->signer_out, header, NULL); - free(header.ptr); + sigheader(this->signer_out, this->seq_out, *type, + this->version, data->len); this->signer_out->allocate_signature(this->signer_out, *data, &mac); if (this->crypter_out) { 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_reader.c b/src/libtls/tls_reader.c deleted file mode 100644 index 2b3cd8cac..000000000 --- a/src/libtls/tls_reader.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2010 Martin Willi - * Copyright (C) 2010 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_reader.h" - -#include <debug.h> - -typedef struct private_tls_reader_t private_tls_reader_t; - -/** - * Private data of an tls_reader_t object. - */ -struct private_tls_reader_t { - - /** - * Public tls_reader_t interface. - */ - tls_reader_t public; - - /** - * Remaining data to process - */ - chunk_t buf; -}; - -METHOD(tls_reader_t, remaining, u_int32_t, - private_tls_reader_t *this) -{ - return this->buf.len; -} - -METHOD(tls_reader_t, peek, chunk_t, - private_tls_reader_t *this) -{ - return this->buf; -} - -METHOD(tls_reader_t, read_uint8, bool, - private_tls_reader_t *this, u_int8_t *res) -{ - if (this->buf.len < 1) - { - DBG1(DBG_TLS, "%d bytes insufficient to parse u_int8 data", - this->buf.len); - return FALSE; - } - *res = this->buf.ptr[0]; - this->buf = chunk_skip(this->buf, 1); - return TRUE; -} - -METHOD(tls_reader_t, read_uint16, bool, - private_tls_reader_t *this, u_int16_t *res) -{ - if (this->buf.len < 2) - { - DBG1(DBG_TLS, "%d bytes insufficient to parse u_int16 data", - this->buf.len); - return FALSE; - } - *res = untoh16(this->buf.ptr); - this->buf = chunk_skip(this->buf, 2); - return TRUE; -} - -METHOD(tls_reader_t, read_uint24, bool, - private_tls_reader_t *this, u_int32_t *res) -{ - if (this->buf.len < 3) - { - DBG1(DBG_TLS, "%d bytes insufficient to parse u_int24 data", - this->buf.len); - return FALSE; - } - *res = untoh32(this->buf.ptr) >> 8; - this->buf = chunk_skip(this->buf, 3); - return TRUE; -} - -METHOD(tls_reader_t, read_uint32, bool, - private_tls_reader_t *this, u_int32_t *res) -{ - if (this->buf.len < 4) - { - DBG1(DBG_TLS, "%d bytes insufficient to parse u_int32 data", - this->buf.len); - return FALSE; - } - *res = untoh32(this->buf.ptr); - this->buf = chunk_skip(this->buf, 4); - return TRUE; -} - -METHOD(tls_reader_t, read_data, bool, - private_tls_reader_t *this, u_int32_t len, chunk_t *res) -{ - if (this->buf.len < len) - { - DBG1(DBG_TLS, "%d bytes insufficient to parse %d bytes of data", - this->buf.len, len); - return FALSE; - } - *res = chunk_create(this->buf.ptr, len); - this->buf = chunk_skip(this->buf, len); - return TRUE; -} - -METHOD(tls_reader_t, read_data8, bool, - private_tls_reader_t *this, chunk_t *res) -{ - u_int8_t len; - - if (!read_uint8(this, &len)) - { - return FALSE; - } - return read_data(this, len, res); -} - -METHOD(tls_reader_t, read_data16, bool, - private_tls_reader_t *this, chunk_t *res) -{ - u_int16_t len; - - if (!read_uint16(this, &len)) - { - return FALSE; - } - return read_data(this, len, res); -} - -METHOD(tls_reader_t, read_data24, bool, - private_tls_reader_t *this, chunk_t *res) -{ - u_int32_t len; - - if (!read_uint24(this, &len)) - { - return FALSE; - } - return read_data(this, len, res); -} - -METHOD(tls_reader_t, read_data32, bool, - private_tls_reader_t *this, chunk_t *res) -{ - u_int32_t len; - - if (!read_uint32(this, &len)) - { - return FALSE; - } - return read_data(this, len, res); -} - -METHOD(tls_reader_t, destroy, void, - private_tls_reader_t *this) -{ - free(this); -} - -/** - * See header - */ -tls_reader_t *tls_reader_create(chunk_t data) -{ - private_tls_reader_t *this; - - INIT(this, - .public = { - .remaining = _remaining, - .peek = _peek, - .read_uint8 = _read_uint8, - .read_uint16 = _read_uint16, - .read_uint24 = _read_uint24, - .read_uint32 = _read_uint32, - .read_data = _read_data, - .read_data8 = _read_data8, - .read_data16 = _read_data16, - .read_data24 = _read_data24, - .read_data32 = _read_data32, - .destroy = _destroy, - }, - .buf = data, - ); - - return &this->public; -} diff --git a/src/libtls/tls_reader.h b/src/libtls/tls_reader.h deleted file mode 100644 index a8978b486..000000000 --- a/src/libtls/tls_reader.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2010 Martin Willi - * Copyright (C) 2010 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_reader tls_reader - * @{ @ingroup libtls - */ - -#ifndef TLS_READER_H_ -#define TLS_READER_H_ - -typedef struct tls_reader_t tls_reader_t; - -#include <library.h> - -/** - * TLS record parser. - */ -struct tls_reader_t { - - /** - * Get the number of remaining bytes. - * - * @return number of remaining bytes in buffer - */ - u_int32_t (*remaining)(tls_reader_t *this); - - /** - * Peek the remaining data, not consuming any bytes. - * - * @return remaining data - */ - chunk_t (*peek)(tls_reader_t *this); - - /** - * Read a 8-bit integer from the buffer, advance. - * - * @param res pointer to result - * @return TRUE if integer read successfully - */ - bool (*read_uint8)(tls_reader_t *this, u_int8_t *res); - - /** - * Read a 16-bit integer from the buffer, advance. - * - * @param res pointer to result - * @return TRUE if integer read successfully - */ - bool (*read_uint16)(tls_reader_t *this, u_int16_t *res); - - /** - * Read a 24-bit integer from the buffer, advance. - * - * @param res pointer to result - * @return TRUE if integer read successfully - */ - bool (*read_uint24)(tls_reader_t *this, u_int32_t *res); - - /** - * Read a 32-bit integer from the buffer, advance. - * - * @param res pointer to result - * @return TRUE if integer read successfully - */ - bool (*read_uint32)(tls_reader_t *this, u_int32_t *res); - - /** - * Read a chunk of len bytes, advance. - * - * @param len number of bytes to read - * @param res pointer to result, not cloned - * @return TRUE if data read successfully - */ - bool (*read_data)(tls_reader_t *this, u_int32_t len, chunk_t *res); - - /** - * Read a chunk of bytes with a 8-bit length header, advance. - * - * @param res pointer to result, not cloned - * @return TRUE if data read successfully - */ - bool (*read_data8)(tls_reader_t *this, chunk_t *res); - - /** - * Read a chunk of bytes with a 16-bit length header, advance. - * - * @param res pointer to result, not cloned - * @return TRUE if data read successfully - */ - bool (*read_data16)(tls_reader_t *this, chunk_t *res); - - /** - * Read a chunk of bytes with a 24-bit length header, advance. - * - * @param res pointer to result, not cloned - * @return TRUE if data read successfully - */ - bool (*read_data24)(tls_reader_t *this, chunk_t *res); - - /** - * Read a chunk of bytes with a 32-bit length header, advance. - * - * @param res pointer to result, not cloned - * @return TRUE if data read successfully - */ - bool (*read_data32)(tls_reader_t *this, chunk_t *res); - - /** - * Destroy a tls_reader_t. - */ - void (*destroy)(tls_reader_t *this); -}; - -/** - * Create a tls_reader instance. - */ -tls_reader_t *tls_reader_create(chunk_t data); - -#endif /** tls_reader_H_ @}*/ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index b0417f6cb..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; @@ -192,13 +206,14 @@ static bool select_suite_and_key(private_tls_server_t *this, * Process client hello message */ static status_t process_client_hello(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { u_int16_t version, extension; chunk_t random, session, ciphers, compression, ext = chunk_empty; - tls_reader_t *extensions; + 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)); @@ -217,7 +232,7 @@ static status_t process_client_hello(private_tls_server_t *this, if (ext.len) { - extensions = tls_reader_create(ext); + extensions = bio_reader_create(ext); while (extensions->remaining(extensions)) { if (!extensions->read_uint16(extensions, &extension) || @@ -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; } @@ -282,10 +328,10 @@ static status_t process_client_hello(private_tls_server_t *this, * Process certificate */ static status_t process_certificate(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { certificate_t *cert; - tls_reader_t *certs; + bio_reader_t *certs; chunk_t data; bool first = TRUE; @@ -298,7 +344,7 @@ static status_t process_certificate(private_tls_server_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - certs = tls_reader_create(data); + certs = bio_reader_create(data); while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) @@ -342,7 +388,7 @@ static status_t process_certificate(private_tls_server_t *this, * Process Client Key Exchange, using premaster encryption */ static status_t process_key_exchange_encrypted(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { chunk_t encrypted, decrypted; char premaster[48]; @@ -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)); @@ -402,7 +449,7 @@ static status_t process_key_exchange_encrypted(private_tls_server_t *this, * Process client key exchange, using DHE exchange */ static status_t process_key_exchange_dhe(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { chunk_t premaster, pub; bool ec; @@ -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); @@ -451,7 +499,7 @@ static status_t process_key_exchange_dhe(private_tls_server_t *this, * Process Client Key Exchange */ static status_t process_key_exchange(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { if (this->dh) { @@ -464,19 +512,19 @@ static status_t process_key_exchange(private_tls_server_t *this, * Process Certificate verify */ static status_t process_cert_verify(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { bool verified = FALSE; enumerator_t *enumerator; public_key_t *public; auth_cfg_t *auth; - tls_reader_t *sig; + bio_reader_t *sig; enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, KEY_ANY, this->peer, this->peer_auth); while (enumerator->enumerate(enumerator, &public, &auth)) { - sig = tls_reader_create(reader->peek(reader)); + sig = bio_reader_create(reader->peek(reader)); verified = this->crypto->verify_handshake(this->crypto, public, sig); sig->destroy(sig); if (verified) @@ -505,7 +553,7 @@ static status_t process_cert_verify(private_tls_server_t *this, * Process finished message */ static status_t process_finished(private_tls_server_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { chunk_t received; char buf[12]; @@ -535,7 +583,7 @@ static status_t process_finished(private_tls_server_t *this, } METHOD(tls_handshake_t, process, status_t, - private_tls_server_t *this, tls_handshake_type_t type, tls_reader_t *reader) + private_tls_server_t *this, tls_handshake_type_t type, bio_reader_t *reader) { tls_handshake_type_t expected; @@ -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) { @@ -603,29 +648,14 @@ METHOD(tls_handshake_t, process, status_t, * Send ServerHello message */ static status_t send_server_hello(private_tls_server_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + 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); @@ -643,16 +673,16 @@ static status_t send_server_hello(private_tls_server_t *this, * Send Certificate */ static status_t send_certificate(private_tls_server_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { enumerator_t *enumerator; certificate_t *cert; auth_rule_t rule; - tls_writer_t *certs; + bio_writer_t *certs; chunk_t data; /* generate certificate payload */ - certs = tls_writer_create(256); + certs = bio_writer_create(256); cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT); if (cert) { @@ -693,15 +723,15 @@ static status_t send_certificate(private_tls_server_t *this, * Send Certificate Request */ static status_t send_certificate_request(private_tls_server_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { - tls_writer_t *authorities, *supported; + bio_writer_t *authorities, *supported; enumerator_t *enumerator; certificate_t *cert; x509_t *x509; identification_t *id; - supported = tls_writer_create(4); + supported = bio_writer_create(4); /* we propose both RSA and ECDSA */ supported->write_uint8(supported, TLS_RSA_SIGN); supported->write_uint8(supported, TLS_ECDSA_SIGN); @@ -712,7 +742,7 @@ static status_t send_certificate_request(private_tls_server_t *this, this->crypto->get_signature_algorithms(this->crypto, writer); } - authorities = tls_writer_create(64); + authorities = bio_writer_create(64); enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509, KEY_RSA, NULL, TRUE); while (enumerator->enumerate(enumerator, &cert)) @@ -763,14 +793,14 @@ static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this, */ bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve) { - tls_reader_t *reader; + bio_reader_t *reader; u_int16_t current; if (!this->curves_received) { /* none received, assume yes */ return TRUE; } - reader = tls_reader_create(this->curves); + reader = bio_reader_create(this->curves); while (reader->remaining(reader) && reader->read_uint16(reader, ¤t)) { if (current == curve) @@ -810,7 +840,7 @@ static bool find_supported_curve(private_tls_server_t *this, * Send Server key Exchange */ static status_t send_server_key_exchange(private_tls_server_t *this, - tls_handshake_type_t *type, tls_writer_t *writer, + tls_handshake_type_t *type, bio_writer_t *writer, diffie_hellman_group_t group) { diffie_hellman_params_t *params = NULL; @@ -887,7 +917,7 @@ static status_t send_server_key_exchange(private_tls_server_t *this, * Send Hello Done */ static status_t send_hello_done(private_tls_server_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { *type = TLS_SERVER_HELLO_DONE; this->state = STATE_HELLO_DONE; @@ -899,7 +929,7 @@ static status_t send_hello_done(private_tls_server_t *this, * Send Finished */ static status_t send_finished(private_tls_server_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { char buf[12]; @@ -914,14 +944,13 @@ 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; } METHOD(tls_handshake_t, build, status_t, - private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer) + private_tls_server_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { diffie_hellman_group_t group; @@ -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 e0c440a4c..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; @@ -67,7 +79,7 @@ struct private_tls_socket_t { }; METHOD(tls_application_t, process, status_t, - private_tls_application_t *this, tls_reader_t *reader) + private_tls_application_t *this, bio_reader_t *reader) { chunk_t data; @@ -80,7 +92,7 @@ METHOD(tls_application_t, process, status_t, } METHOD(tls_application_t, build, status_t, - private_tls_application_t *this, tls_writer_t *writer) + private_tls_application_t *this, bio_writer_t *writer) { if (this->out.len) { @@ -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/libtls/tls_writer.c b/src/libtls/tls_writer.c deleted file mode 100644 index 57c60fdaf..000000000 --- a/src/libtls/tls_writer.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2010 Martin Willi - * Copyright (C) 2010 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_writer.h" - -typedef struct private_tls_writer_t private_tls_writer_t; - -/** - * Private data of an tls_writer_t object. - */ -struct private_tls_writer_t { - - /** - * Public tls_writer_t interface. - */ - tls_writer_t public; - - /** - * Allocated buffer - */ - chunk_t buf; - - /** - * Used bytes in buffer - */ - size_t used; - - /** - * Number of bytes to increase buffer size - */ - size_t increase; -}; - -/** - * Increase buffer size - */ -static void increase(private_tls_writer_t *this) -{ - this->buf.len += this->increase; - this->buf.ptr = realloc(this->buf.ptr, this->buf.len); -} - -METHOD(tls_writer_t, write_uint8, void, - private_tls_writer_t *this, u_int8_t value) -{ - if (this->used + 1 > this->buf.len) - { - increase(this); - } - this->buf.ptr[this->used] = value; - this->used += 1; -} - -METHOD(tls_writer_t, write_uint16, void, - private_tls_writer_t *this, u_int16_t value) -{ - if (this->used + 2 > this->buf.len) - { - increase(this); - } - htoun16(this->buf.ptr + this->used, value); - this->used += 2; -} - -METHOD(tls_writer_t, write_uint24, void, - private_tls_writer_t *this, u_int32_t value) -{ - if (this->used + 3 > this->buf.len) - { - increase(this); - } - value = htonl(value); - memcpy(this->buf.ptr + this->used, ((char*)&value) + 1, 3); - this->used += 3; -} - -METHOD(tls_writer_t, write_uint32, void, - private_tls_writer_t *this, u_int32_t value) -{ - if (this->used + 4 > this->buf.len) - { - increase(this); - } - htoun32(this->buf.ptr + this->used, value); - this->used += 4; -} - -METHOD(tls_writer_t, write_data, void, - private_tls_writer_t *this, chunk_t value) -{ - while (this->used + value.len > this->buf.len) - { - increase(this); - } - memcpy(this->buf.ptr + this->used, value.ptr, value.len); - this->used += value.len; -} - -METHOD(tls_writer_t, write_data8, void, - private_tls_writer_t *this, chunk_t value) -{ - write_uint8(this, value.len); - write_data(this, value); -} - -METHOD(tls_writer_t, write_data16, void, - private_tls_writer_t *this, chunk_t value) -{ - write_uint16(this, value.len); - write_data(this, value); -} - -METHOD(tls_writer_t, write_data24, void, - private_tls_writer_t *this, chunk_t value) -{ - write_uint24(this, value.len); - write_data(this, value); -} - -METHOD(tls_writer_t, write_data32, void, - private_tls_writer_t *this, chunk_t value) -{ - write_uint32(this, value.len); - write_data(this, value); -} - -METHOD(tls_writer_t, wrap8, void, - private_tls_writer_t *this) -{ - if (this->used + 1 > this->buf.len) - { - increase(this); - } - memmove(this->buf.ptr + 1, this->buf.ptr, this->used); - this->buf.ptr[0] = this->used; - this->used += 1; -} - -METHOD(tls_writer_t, wrap16, void, - private_tls_writer_t *this) -{ - if (this->used + 2 > this->buf.len) - { - increase(this); - } - memmove(this->buf.ptr + 2, this->buf.ptr, this->used); - htoun16(this->buf.ptr, this->used); - this->used += 2; -} - -METHOD(tls_writer_t, wrap24, void, - private_tls_writer_t *this) -{ - u_int32_t len; - - if (this->used + 3 > this->buf.len) - { - increase(this); - } - memmove(this->buf.ptr + 3, this->buf.ptr, this->used); - - len = htonl(this->used); - memcpy(this->buf.ptr, ((char*)&len) + 1, 3); - this->used += 3; -} - -METHOD(tls_writer_t, wrap32, void, - private_tls_writer_t *this) -{ - if (this->used + 4 > this->buf.len) - { - increase(this); - } - memmove(this->buf.ptr + 4, this->buf.ptr, this->used); - htoun32(this->buf.ptr, this->used); - this->used += 4; -} - -METHOD(tls_writer_t, get_buf, chunk_t, - private_tls_writer_t *this) -{ - return chunk_create(this->buf.ptr, this->used); -} - -METHOD(tls_writer_t, destroy, void, - private_tls_writer_t *this) -{ - free(this->buf.ptr); - free(this); -} - -/** - * See header - */ -tls_writer_t *tls_writer_create(u_int32_t bufsize) -{ - private_tls_writer_t *this; - - INIT(this, - .public = { - .write_uint8 = _write_uint8, - .write_uint16 = _write_uint16, - .write_uint24 = _write_uint24, - .write_uint32 = _write_uint32, - .write_data = _write_data, - .write_data8 = _write_data8, - .write_data16 = _write_data16, - .write_data24 = _write_data24, - .write_data32 = _write_data32, - .wrap8 = _wrap8, - .wrap16 = _wrap16, - .wrap24 = _wrap24, - .wrap32 = _wrap32, - .get_buf = _get_buf, - .destroy = _destroy, - }, - .increase = bufsize ? max(bufsize, 4) : 32, - ); - if (bufsize) - { - this->buf = chunk_alloc(bufsize); - } - - return &this->public; -} diff --git a/src/libtls/tls_writer.h b/src/libtls/tls_writer.h deleted file mode 100644 index d3f09d5da..000000000 --- a/src/libtls/tls_writer.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2010 Martin Willi - * Copyright (C) 2010 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_writer tls_writer - * @{ @ingroup libtls - */ - -#ifndef TLS_WRITER_H_ -#define TLS_WRITER_H_ - -typedef struct tls_writer_t tls_writer_t; - -#include <library.h> - -/** - * TLS record generator. - */ -struct tls_writer_t { - - /** - * Append a 8-bit integer to the buffer. - * - * @param value value to append - */ - void (*write_uint8)(tls_writer_t *this, u_int8_t value); - - /** - * Append a 16-bit integer to the buffer. - * - * @param value value to append - */ - void (*write_uint16)(tls_writer_t *this, u_int16_t value); - - /** - * Append a 24-bit integer to the buffer. - * - * @param value value to append - */ - void (*write_uint24)(tls_writer_t *this, u_int32_t value); - - /** - * Append a 32-bit integer to the buffer. - * - * @param value value to append - */ - void (*write_uint32)(tls_writer_t *this, u_int32_t value); - - /** - * Append a chunk of data without a length header. - * - * @param value value to append - */ - void (*write_data)(tls_writer_t *this, chunk_t value); - - /** - * Append a chunk of data with a 8-bit length header. - * - * @param value value to append - */ - void (*write_data8)(tls_writer_t *this, chunk_t value); - - /** - * Append a chunk of data with a 16-bit length header. - * - * @param value value to append - */ - void (*write_data16)(tls_writer_t *this, chunk_t value); - - /** - * Append a chunk of data with a 24-bit length header. - * - * @param value value to append - */ - void (*write_data24)(tls_writer_t *this, chunk_t value); - - /** - * Append a chunk of data with a 32-bit length header. - * - * @param value value to append - */ - void (*write_data32)(tls_writer_t *this, chunk_t value); - - /** - * Prepend a 8-bit length header to existing data. - */ - void (*wrap8)(tls_writer_t *this); - - /** - * Prepend a 16-bit length header to existing data. - */ - void (*wrap16)(tls_writer_t *this); - - /** - * Prepend a 24-bit length header to existing data. - */ - void (*wrap24)(tls_writer_t *this); - - /** - * Prepend a 32-bit length header to existing data. - */ - void (*wrap32)(tls_writer_t *this); - - /** - * Get the encoded data buffer. - * - * @return chunk to internal buffer - */ - chunk_t (*get_buf)(tls_writer_t *this); - - /** - * Destroy a tls_writer_t. - */ - void (*destroy)(tls_writer_t *this); -}; - -/** - * Create a tls_writer instance. - * - * @param bufsize initially allocated buffer size - */ -tls_writer_t *tls_writer_create(u_int32_t bufsize); - -#endif /** TLS_WRITER_H_ @}*/ |