diff options
Diffstat (limited to 'src/charon/plugins/eap_aka')
-rw-r--r-- | src/charon/plugins/eap_aka/Makefile.am | 13 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/Makefile.in | 183 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka.c | 1553 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka.h | 81 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_peer.c | 583 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_peer.h | 49 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_plugin.c | 19 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_plugin.h | 7 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_server.c | 700 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_server.h | 49 |
10 files changed, 1528 insertions, 1709 deletions
diff --git a/src/charon/plugins/eap_aka/Makefile.am b/src/charon/plugins/eap_aka/Makefile.am index 1a3ea1857..e007f5f00 100644 --- a/src/charon/plugins/eap_aka/Makefile.am +++ b/src/charon/plugins/eap_aka/Makefile.am @@ -1,11 +1,14 @@ -INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon \ + -I$(top_srcdir)/src/libsimaka AM_CFLAGS = -rdynamic -plugin_LTLIBRARIES = libstrongswan-eapaka.la +plugin_LTLIBRARIES = libstrongswan-eap-aka.la -libstrongswan_eapaka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c eap_aka.h eap_aka.c -libstrongswan_eapaka_la_LDFLAGS = -module -avoid-version -libstrongswan_eapaka_la_LIBADD = -lgmp +libstrongswan_eap_aka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c \ + eap_aka_peer.h eap_aka_peer.c \ + eap_aka_server.h eap_aka_server.c +libstrongswan_eap_aka_la_LIBADD = $(top_builddir)/src/libsimaka/libsimaka.la +libstrongswan_eap_aka_la_LDFLAGS = -module -avoid-version diff --git a/src/charon/plugins/eap_aka/Makefile.in b/src/charon/plugins/eap_aka/Makefile.in index 2d2405379..d241e1ad0 100644 --- a/src/charon/plugins/eap_aka/Makefile.in +++ b/src/charon/plugins/eap_aka/Makefile.in @@ -1,8 +1,9 @@ -# Makefile.in generated by automake 1.10.2 from Makefile.am. +# Makefile.in generated by automake 1.11 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,8 +17,9 @@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c @@ -35,30 +37,55 @@ host_triplet = @host@ subdir = src/charon/plugins/eap_aka DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ + $(top_srcdir)/m4/config/ltoptions.m4 \ + $(top_srcdir)/m4/config/ltsugar.m4 \ + $(top_srcdir)/m4/config/ltversion.m4 \ + $(top_srcdir)/m4/config/lt~obsolete.m4 \ + $(top_srcdir)/m4/macros/with.m4 \ + $(top_srcdir)/m4/macros/enable-disable.m4 \ + $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = 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 = `echo $$p | sed -e 's|^.*/||'`; +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)$(plugindir)" -pluginLTLIBRARIES_INSTALL = $(INSTALL) LTLIBRARIES = $(plugin_LTLIBRARIES) -libstrongswan_eapaka_la_DEPENDENCIES = -am_libstrongswan_eapaka_la_OBJECTS = eap_aka_plugin.lo eap_aka.lo -libstrongswan_eapaka_la_OBJECTS = \ - $(am_libstrongswan_eapaka_la_OBJECTS) -libstrongswan_eapaka_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ +libstrongswan_eap_aka_la_DEPENDENCIES = \ + $(top_builddir)/src/libsimaka/libsimaka.la +am_libstrongswan_eap_aka_la_OBJECTS = eap_aka_plugin.lo \ + eap_aka_peer.lo eap_aka_server.lo +libstrongswan_eap_aka_la_OBJECTS = \ + $(am_libstrongswan_eap_aka_la_OBJECTS) +libstrongswan_eap_aka_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(libstrongswan_eapaka_la_LDFLAGS) $(LDFLAGS) -o $@ + $(libstrongswan_eap_aka_la_LDFLAGS) $(LDFLAGS) -o $@ DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles +am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ @@ -68,8 +95,8 @@ CCLD = $(CC) LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ -SOURCES = $(libstrongswan_eapaka_la_SOURCES) -DIST_SOURCES = $(libstrongswan_eapaka_la_SOURCES) +SOURCES = $(libstrongswan_eap_aka_la_SOURCES) +DIST_SOURCES = $(libstrongswan_eap_aka_la_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -106,25 +133,22 @@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -IPSEC_ROUTING_TABLE = @IPSEC_ROUTING_TABLE@ -IPSEC_ROUTING_TABLE_PRIO = @IPSEC_ROUTING_TABLE_PRIO@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LEXLIB = @LEXLIB@ LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ -LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ -LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ -LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ -LINUX_HEADERS = @LINUX_HEADERS@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ +MYSQLCFLAG = @MYSQLCFLAG@ +MYSQLCONFIG = @MYSQLCONFIG@ +MYSQLLIB = @MYSQLLIB@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ @@ -136,11 +160,14 @@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ +PTHREADLIB = @PTHREADLIB@ RANLIB = @RANLIB@ +RTLIB = @RTLIB@ RUBY = @RUBY@ RUBYINCLUDE = @RUBYINCLUDE@ SED = @SED@ @@ -169,9 +196,9 @@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ -confdir = @confdir@ datadir = @datadir@ datarootdir = @datarootdir@ +default_pkcs11 = @default_pkcs11@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -194,7 +221,7 @@ ipsecuser = @ipsecuser@ libdir = @libdir@ libexecdir = @libexecdir@ libstrongswan_plugins = @libstrongswan_plugins@ -linuxdir = @linuxdir@ +linux_headers = @linux_headers@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ @@ -202,6 +229,7 @@ mandir = @mandir@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ +nm_ca_dir = @nm_ca_dir@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ piddir = @piddir@ @@ -210,10 +238,12 @@ pluto_plugins = @pluto_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +random_device = @random_device@ resolv_conf = @resolv_conf@ +routing_table = @routing_table@ +routing_table_prio = @routing_table_prio@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ -simreader = @simreader@ srcdir = @srcdir@ strongswan_conf = @strongswan_conf@ sysconfdir = @sysconfdir@ @@ -221,14 +251,20 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ +urandom_device = @urandom_device@ xml_CFLAGS = @xml_CFLAGS@ xml_LIBS = @xml_LIBS@ -INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon +INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon \ + -I$(top_srcdir)/src/libsimaka + AM_CFLAGS = -rdynamic -plugin_LTLIBRARIES = libstrongswan-eapaka.la -libstrongswan_eapaka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c eap_aka.h eap_aka.c -libstrongswan_eapaka_la_LDFLAGS = -module -avoid-version -libstrongswan_eapaka_la_LIBADD = -lgmp +plugin_LTLIBRARIES = libstrongswan-eap-aka.la +libstrongswan_eap_aka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c \ + eap_aka_peer.h eap_aka_peer.c \ + eap_aka_server.h eap_aka_server.c + +libstrongswan_eap_aka_la_LIBADD = $(top_builddir)/src/libsimaka/libsimaka.la +libstrongswan_eap_aka_la_LDFLAGS = -module -avoid-version all: all-am .SUFFIXES: @@ -242,9 +278,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/charon/plugins/eap_aka/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/charon/plugins/eap_aka/Makefile + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/charon/plugins/eap_aka/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/charon/plugins/eap_aka/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ @@ -262,23 +298,28 @@ $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)" - @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \ + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ if test -f $$p; then \ - f=$(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pluginLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(plugindir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pluginLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(plugindir)/$$f"; \ + list2="$$list2 $$p"; \ else :; fi; \ - done + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } uninstall-pluginLTLIBRARIES: @$(NORMAL_UNINSTALL) - @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \ - p=$(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$p'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$p"; \ + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ done clean-pluginLTLIBRARIES: @@ -289,8 +330,8 @@ clean-pluginLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done -libstrongswan-eapaka.la: $(libstrongswan_eapaka_la_OBJECTS) $(libstrongswan_eapaka_la_DEPENDENCIES) - $(libstrongswan_eapaka_la_LINK) -rpath $(plugindir) $(libstrongswan_eapaka_la_OBJECTS) $(libstrongswan_eapaka_la_LIBADD) $(LIBS) +libstrongswan-eap-aka.la: $(libstrongswan_eap_aka_la_OBJECTS) $(libstrongswan_eap_aka_la_DEPENDENCIES) + $(libstrongswan_eap_aka_la_LINK) -rpath $(plugindir) $(libstrongswan_eap_aka_la_OBJECTS) $(libstrongswan_eap_aka_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -298,26 +339,27 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_aka.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_aka_peer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_aka_plugin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eap_aka_server.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< @@ -340,7 +382,7 @@ tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) - tags=; \ + set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ @@ -348,29 +390,34 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ - if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) - tags=; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ - test -z "$(CTAGS_ARGS)$$tags$$unique" \ + test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$tags $$unique + $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ - && cd $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) $$here + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags @@ -391,13 +438,17 @@ distdir: $(DISTFILES) if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @@ -428,6 +479,7 @@ clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -449,6 +501,8 @@ dvi-am: html: html-am +html-am: + info: info-am info-am: @@ -457,18 +511,28 @@ install-data-am: install-pluginLTLIBRARIES install-dvi: install-dvi-am +install-dvi-am: + install-exec-am: install-html: install-html-am +install-html-am: + install-info: install-info-am +install-info-am: + install-man: install-pdf: install-pdf-am +install-pdf-am: + install-ps: install-ps-am +install-ps-am: + installcheck-am: maintainer-clean: maintainer-clean-am @@ -507,6 +571,7 @@ uninstall-am: uninstall-pluginLTLIBRARIES mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-pluginLTLIBRARIES + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/src/charon/plugins/eap_aka/eap_aka.c b/src/charon/plugins/eap_aka/eap_aka.c deleted file mode 100644 index 82ee6c3f0..000000000 --- a/src/charon/plugins/eap_aka/eap_aka.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* - * Copyright (C) 2006 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - - -/* The EAP-AKA method uses it's own simple parser for processing EAP-AKA - * payloads, as the IKEv2 parser is not suitable for that job. There are - * two simple methods for parsing payloads, read_header() and read_attribute(). - * Every EAP-AKA payload consists of a header and a list of attributes. Those - * functions mentioned read the data and return the type of the found - * attribute/EAP-AKA-type. For generating a EAP-AKA message, we have a - * build_aka_payload(), which builds the whole message from a variable - * argument list containing its attributes. - * The processing of messages is split up in various functions: - * - peer_process() - General processing multiplexer for the peer - * - peer_process_challenge() - Specific AKA-Challenge processor - * - peer_process_notification() - Processing of AKA-Notification - * - server_process() - General processing multiplexer for the server - * - peer_process_challenge() - Processing of a received Challenge response - * - peer_process_synchronize() - Process a sequence number synchronization - * - server_initiate() - Initiation method for the server, calls - * - server_initiate_challenge() - Initiation of AKA-Challenge - */ - -#include <limits.h> -#include <string.h> -#include <unistd.h> -#include <sys/time.h> -#include <time.h> -#include <gmp.h> - -#include "eap_aka.h" - -#include <daemon.h> -#include <library.h> -#include <crypto/hashers/hasher.h> - -/* Use test vectors specified in S.S0055 -#define TEST_VECTORS */ - -#define RAND_LENGTH 16 -#define RES_LENGTH 16 -#define SQN_LENGTH 6 -#define K_LENGTH 16 -#define MAC_LENGTH 8 -#define CK_LENGTH 16 -#define IK_LENGTH 16 -#define AK_LENGTH 6 -#define AMF_LENGTH 2 -#define FMK_LENGTH 4 -#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH) -#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH) -#define PAYLOAD_LENGTH 64 -#define MK_LENGTH 20 -#define MSK_LENGTH 64 -#define EMSK_LENGTH 64 -#define KAUTH_LENGTH 16 -#define KENCR_LENGTH 16 -#define AT_MAC_LENGTH 16 - -#define F1 0x42 -#define F1STAR 0x43 -#define F2 0x44 -#define F3 0x45 -#define F4 0x46 -#define F5 0x47 -#define F5STAR 0x48 - -typedef enum aka_subtype_t aka_subtype_t; -typedef enum aka_attribute_t aka_attribute_t; - -/** - * Subtypes of AKA messages - */ -enum aka_subtype_t { - AKA_CHALLENGE = 1, - AKA_AUTHENTICATION_REJECT = 2, - AKA_SYNCHRONIZATION_FAILURE = 4, - AKA_IDENTITY = 5, - AKA_NOTIFICATION = 12, - AKA_REAUTHENTICATION = 13, - AKA_CLIENT_ERROR = 14, -}; - -/** - * Attribute types in AKA messages - */ -enum aka_attribute_t { - /** defines the end of attribute list */ - AT_END = -1, - AT_RAND = 1, - AT_AUTN = 2, - AT_RES = 3, - AT_AUTS = 4, - AT_PADDING = 6, - AT_NONCE_MT = 7, - AT_PERMANENT_ID_REQ = 10, - AT_MAC = 11, - AT_NOTIFICATION = 12, - AT_ANY_ID_REQ = 13, - AT_IDENTITY = 14, - AT_VERSION_LIST = 15, - AT_SELECTED_VERSION = 16, - AT_FULLAUTH_ID_REQ = 17, - AT_COUNTER = 19, - AT_COUNTER_TOO_SMALL = 20, - AT_NONCE_S = 21, - AT_CLIENT_ERROR_CODE = 22, - AT_IV = 129, - AT_ENCR_DATA = 130, - AT_NEXT_PSEUDONYM = 132, - AT_NEXT_REAUTH_ID = 133, - AT_CHECKCODE = 134, - AT_RESULT_IND = 135, -}; - -ENUM_BEGIN(aka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY, - "AKA_CHALLENGE", - "AKA_AUTHENTICATION_REJECT", - "AKA_3", - "AKA_SYNCHRONIZATION_FAILURE", - "AKA_IDENTITY"); -ENUM_NEXT(aka_subtype_names, AKA_NOTIFICATION, AKA_CLIENT_ERROR, AKA_IDENTITY, - "AKA_NOTIFICATION", - "AKA_REAUTHENTICATION", - "AKA_CLIENT_ERROR"); -ENUM_END(aka_subtype_names, AKA_CLIENT_ERROR); - - -ENUM_BEGIN(aka_attribute_names, AT_END, AT_CLIENT_ERROR_CODE, - "AT_END", - "AT_0", - "AT_RAND", - "AT_AUTN", - "AT_RES", - "AT_AUTS", - "AT_5", - "AT_PADDING", - "AT_NONCE_MT", - "AT_8", - "AT_9", - "AT_PERMANENT_ID_REQ", - "AT_MAC", - "AT_NOTIFICATION", - "AT_ANY_ID_REQ", - "AT_IDENTITY", - "AT_VERSION_LIST", - "AT_SELECTED_VERSION", - "AT_FULLAUTH_ID_REQ", - "AT_18", - "AT_COUNTER", - "AT_COUNTER_TOO_SMALL", - "AT_NONCE_S", - "AT_CLIENT_ERROR_CODE"); -ENUM_NEXT(aka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE, - "AT_IV", - "AT_ENCR_DATA", - "AT_131", - "AT_NEXT_PSEUDONYM", - "AT_NEXT_REAUTH_ID", - "AT_CHECKCODE", - "AT_RESULT_IND"); -ENUM_END(aka_attribute_names, AT_RESULT_IND); - - -typedef struct private_eap_aka_t private_eap_aka_t; - -/** - * Private data of an eap_aka_t object. - */ -struct private_eap_aka_t { - - /** - * Public authenticator_t interface. - */ - eap_aka_t public; - - /** - * ID of the server - */ - identification_t *server; - - /** - * ID of the peer - */ - identification_t *peer; - - /** - * SHA11 hasher - */ - hasher_t *sha1; - - /** - * MAC function used in EAP-AKA - */ - signer_t *signer; - - /** - * pseudo random function used in EAP-aka - */ - prf_t *prf; - - /** - * Special keyed SHA1 hasher used in EAP-AKA, implemented as PRF - */ - prf_t *keyed_prf; - - /** - * Key for EAP MAC - */ - chunk_t k_auth; - - /** - * Key for EAP encryption - */ - chunk_t k_encr; - - /** - * MSK - */ - chunk_t msk; - - /** - * Extendend MSK - */ - chunk_t emsk; - - /** - * Expected result from client XRES - */ - chunk_t xres; - - /** - * Shared secret K from ipsec.conf (padded) - */ - chunk_t k; - - /** - * random value RAND generated by server - */ - chunk_t rand; -}; - -/** Family key, as proposed in S.S0055 */ -static u_int8_t fmk_buf[] = {0x41, 0x48, 0x41, 0x47}; -static chunk_t fmk = chunk_from_buf(fmk_buf); - -/** Authentication management field */ -static u_int8_t amf_buf[] = {0x00, 0x01}; -static chunk_t amf = chunk_from_buf(amf_buf); - -/** AT_CLIENT_ERROR_CODE AKA attribute */ -static u_int8_t client_error_code_buf[] = {0, 0}; -static chunk_t client_error_code = chunk_from_buf(client_error_code_buf); - -/** previously used sqn by peer, next one must be greater */ -static u_int8_t peer_sqn_buf[6]; -static chunk_t peer_sqn = chunk_from_buf(peer_sqn_buf); - -/** set SQN to the current time */ -static void update_sqn(u_int8_t *sqn, time_t offset) -{ - timeval_t time; - gettimeofday(&time, NULL); - /* set sqb_sqn to an integer containing seconds followed by most - * significant useconds */ - time.tv_sec = htonl(time.tv_sec + offset); - /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */ - time.tv_usec <<= 12; - time.tv_usec = htonl(time.tv_usec); - memcpy(sqn, &time.tv_sec, 4); - memcpy(sqn + 4, &time.tv_usec, 2); -} - -/** initialize peers SQN to the current system time at startup */ -static void __attribute__ ((constructor))init_sqn(void) -{ - update_sqn(peer_sqn_buf, 0); -} - -/** - * Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1 - */ -static u_int8_t g[] = { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2d -}; - -/** - * Predefined random bits from the RAND Corporation book - */ -static u_int8_t a[] = { - 0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11, - 0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49, - 0x3f, 0x4c, 0x63, 0x65 -}; - -/** - * Predefined random bits from the RAND Corporation book - */ -static u_int8_t b[] = { - 0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51, - 0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e, - 0x7e, 0xec, 0x45, 0xe0 -}; - -/** - * Multiplicate two mpz_t with bits interpreted as polynoms. - */ -static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b) -{ - mpz_t bm, rm; - int current = 0, shifted = 0, shift; - - mpz_init_set(bm, b); - mpz_init_set_ui(rm, 0); - /* scan through a, for each found bit: */ - while ((current = mpz_scan1(a, current)) != ULONG_MAX) - { - /* XOR shifted b into r */ - shift = current - shifted; - mpz_mul_2exp(bm, bm, shift); - shifted += shift; - mpz_xor(rm, rm, bm); - current++; - } - - mpz_swap(r, rm); - mpz_clear(rm); - mpz_clear(bm); -} - -/** - * Calculate the sum of a + b interpreted as polynoms. - */ -static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b) -{ - /* addition of polynominals is just the XOR */ - mpz_xor(res, a, b); -} - -/** - * Calculate the remainder of a/b interpreted as polynoms. - */ -static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b) -{ - /* Example: - * a = 10001010 - * b = 00000101 - */ - int a_bit, b_bit, diff; - mpz_t bm, am; - - mpz_init_set(am, a); - mpz_init(bm); - - a_bit = mpz_sizeinbase(a, 2); - b_bit = mpz_sizeinbase(b, 2); - - /* don't do anything if b > a */ - if (a_bit >= b_bit) - { - /* shift b left to align up most signaficant "1" to a: - * a = 10001010 - * b = 10100000 - */ - mpz_mul_2exp(bm, b, a_bit - b_bit); - do - { - /* XOR b into a, this kills the most significant "1": - * a = 00101010 - */ - mpz_xor(am, am, bm); - /* find the next most significant "1" in a, and align up b: - * a = 00101010 - * b = 00101000 - */ - diff = a_bit - mpz_sizeinbase(am, 2); - mpz_div_2exp(bm, bm, diff); - a_bit -= diff; - } - while (b_bit <= mpz_sizeinbase(bm, 2)); - /* While b is not shifted to its original value */ - } - /* after another iteration: - * a = 00000010 - * which is the polynomial modulo - */ - - mpz_swap(r, am); - mpz_clear(am); - mpz_clear(bm); -} - -/** - * Step 4 of the various fx() functions: - * Polynomial whiten calculations - */ -static void step4(private_eap_aka_t *this, u_int8_t x[]) -{ - mpz_t xm, am, bm, gm; - - mpz_init(xm); - mpz_init(am); - mpz_init(bm); - mpz_init(gm); - - mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x); - mpz_import(am, sizeof(a), 1, 1, 1, 0, a); - mpz_import(bm, sizeof(b), 1, 1, 1, 0, b); - mpz_import(gm, sizeof(g), 1, 1, 1, 0, g); - - mpz_mul_poly(xm, am, xm); - mpz_add_poly(xm, bm, xm); - mpz_mod_poly(xm, xm, gm); - - mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm); - - mpz_clear(xm); - mpz_clear(am); - mpz_clear(bm); - mpz_clear(gm); -} - -/** - * Step 3 of the various fx() functions: - * XOR the key into the SHA1 IV - */ -static void step3(private_eap_aka_t *this, - chunk_t k, chunk_t payload, u_int8_t h[]) -{ - u_int8_t buf[64]; - - if (payload.len < sizeof(buf)) - { - /* pad c with zeros */ - memset(buf, 0, sizeof(buf)); - memcpy(buf, payload.ptr, payload.len); - payload.ptr = buf; - payload.len = sizeof(buf); - } - else - { - /* not more than 512 bits can be G()-ed */ - payload.len = sizeof(buf); - } - - /* use the keyed hasher to build the hash */ - this->keyed_prf->set_key(this->keyed_prf, k); - this->keyed_prf->get_bytes(this->keyed_prf, payload, h); -} - -/** - * Calculation function for f2(), f3(), f4() - */ -static void fx(private_eap_aka_t *this, - u_int8_t f, chunk_t k, chunk_t rand, u_int8_t out[]) -{ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - u_int8_t i; - - for (i = 0; i < 2; i++) - { - memset(payload.ptr, 0x5c, payload.len); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 24, rand.ptr, rand.len); - - payload.ptr[3] ^= i; - payload.ptr[19] ^= i; - payload.ptr[35] ^= i; - payload.ptr[51] ^= i; - - step3(this, k, payload, h); - step4(this, h); - memcpy(out + i * 8, h, 8); - } -} - -/** - * Calculation function of f1() and f1star() - */ -static void f1x(private_eap_aka_t *this, - u_int8_t f, chunk_t k, chunk_t rand, chunk_t sqn, - chunk_t amf, u_int8_t mac[]) -{ - /* generate MAC = f1(FMK, SQN, RAND, AMF) - * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit - * payload which gets hashed - */ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - - memset(payload.ptr, 0x5c, PAYLOAD_LENGTH); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 16, rand.ptr, rand.len); - memxor(payload.ptr + 34, sqn.ptr, sqn.len); - memxor(payload.ptr + 42, amf.ptr, amf.len); - - step3(this, k, payload, h); - step4(this, h); - memcpy(mac, h, MAC_LENGTH); -} - -/** - * Calculation function of f5() and f5star() - */ -static void f5x(private_eap_aka_t *this, - u_int8_t f, chunk_t k, chunk_t rand, u_int8_t ak[]) -{ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - - memset(payload.ptr, 0x5c, payload.len); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 16, rand.ptr, rand.len); - - step3(this, k, payload, h); - step4(this, h); - memcpy(ak, h, AK_LENGTH); -} - -/** - * Calculate the MAC from a RAND, SQN, AMF value using K - */ -static void f1(private_eap_aka_t *this, chunk_t k, chunk_t rand, chunk_t sqn, - chunk_t amf, u_int8_t mac[]) -{ - f1x(this, F1, k, rand, sqn, amf, mac); - DBG3(DBG_IKE, "MAC %b", mac, MAC_LENGTH); -} - -/** - * Calculate the MACS from a RAND, SQN, AMF value using K - */ -static void f1star(private_eap_aka_t *this, chunk_t k, chunk_t rand, - chunk_t sqn, chunk_t amf, u_int8_t macs[]) -{ - f1x(this, F1STAR, k, rand, sqn, amf, macs); - DBG3(DBG_IKE, "MACS %b", macs, MAC_LENGTH); -} - -/** - * Calculate RES from RAND using K - */ -static void f2(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t res[]) -{ - fx(this, F2, k, rand, res); - DBG3(DBG_IKE, "RES %b", res, RES_LENGTH); -} - -/** - * Calculate CK from RAND using K - */ -static void f3(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ck[]) -{ - fx(this, F3, k, rand, ck); - DBG3(DBG_IKE, "CK %b", ck, CK_LENGTH); -} - -/** - * Calculate IK from RAND using K - */ -static void f4(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ik[]) -{ - fx(this, F4, k, rand, ik); - DBG3(DBG_IKE, "IK %b", ik, IK_LENGTH); -} - -/** - * Calculate AK from a RAND using K - */ -static void f5(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t ak[]) -{ - f5x(this, F5, k, rand, ak); - DBG3(DBG_IKE, "AK %b", ak, AK_LENGTH); -} - -/** - * Calculate AKS from a RAND using K - */ -static void f5star(private_eap_aka_t *this, chunk_t k, chunk_t rand, u_int8_t aks[]) -{ - f5x(this, F5STAR, k, rand, aks); - DBG3(DBG_IKE, "AKS %b", aks, AK_LENGTH); -} - -/** - * derive the keys needed for EAP_AKA - */ -static bool derive_keys(private_eap_aka_t *this, identification_t *id) -{ - chunk_t ck, ik, mk, identity, tmp; - - ck = chunk_alloca(CK_LENGTH); - ik = chunk_alloca(IK_LENGTH); - mk = chunk_alloca(MK_LENGTH); - identity = id->get_encoding(id); - - /* MK = SHA1( Identity | IK | CK ) */ - f3(this, this->k, this->rand, ck.ptr); - f4(this, this->k, this->rand, ik.ptr); - DBG3(DBG_IKE, "Identity %B", &identity); - tmp = chunk_cata("ccc", identity, ik, ck); - DBG3(DBG_IKE, "Identity|IK|CK %B", &tmp); - this->sha1->get_hash(this->sha1, tmp, mk.ptr); - - /* K_encr | K_auth | MSK | EMSK = prf(0) | prf(0) - * FIPS PRF has 320 bit block size, we need 160 byte for keys - * => run prf four times */ - this->prf->set_key(this->prf, mk); - tmp = chunk_alloca(this->prf->get_block_size(this->prf) * 4); - this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr); - this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1); - this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2); - this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3); - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_split(tmp, "aaaa", 16, &this->k_encr, 16, &this->k_auth, - 64, &this->msk, 64, &this->emsk); - DBG3(DBG_IKE, "MK %B", &mk); - DBG3(DBG_IKE, "PRF res %B", &tmp); - DBG3(DBG_IKE, "K_encr %B", &this->k_encr); - DBG3(DBG_IKE, "K_auth %B", &this->k_auth); - DBG3(DBG_IKE, "MSK %B", &this->msk); - DBG3(DBG_IKE, "EMSK %B", &this->emsk); - return TRUE; -} - -/* - * Get a shared key from ipsec.secrets. - * We use the standard keys as used in preshared key authentication. As - * these keys have an undefined length, we: - * - strip them if they are longer - * - fill them up with '\0' if they are shorter - */ -static status_t load_key(identification_t *me, identification_t *other, chunk_t *k) -{ - shared_key_t *shared; - chunk_t key; - - shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP, - me, other); - if (shared == NULL) - { - return NOT_FOUND; - } - key = shared->get_key(shared); - chunk_free(k); - *k = chunk_alloc(K_LENGTH); - memset(k->ptr, '\0', k->len); - memcpy(k->ptr, key.ptr, min(key.len, k->len)); - shared->destroy(shared); - return SUCCESS; -} - -/** - * skip EAP_AKA header in message and returns its AKA subtype - */ -static aka_subtype_t read_header(chunk_t *message) -{ - aka_subtype_t type; - - if (message->len < 8) - { - *message = chunk_empty; - return 0; - } - type = *(message->ptr + 5); - *message = chunk_skip(*message, 8); - return type; -} - -/** - * read the next attribute from the chunk data - */ -static aka_attribute_t read_attribute(chunk_t *data, chunk_t *attr_data) -{ - aka_attribute_t attribute; - size_t length; - - DBG3(DBG_IKE, "reading attribute from %B", data); - - if (data->len < 2) - { - return AT_END; - } - /* read attribute and length */ - attribute = *data->ptr++; - length = *data->ptr++ * 4 - 2; - data->len -= 2; - DBG3(DBG_IKE, "found attribute %N with length %d", - aka_attribute_names, attribute, length); - if (length > data->len) - { - return AT_END; - } - /* apply attribute value to attr_data */ - attr_data->len = length; - attr_data->ptr = data->ptr; - /* update data to point to next attribute */ - *data = chunk_skip(*data, length); - return attribute; -} - -/** - * Build an AKA payload from different attributes. - * The variable argument takes an aka_attribute_t - * followed by its data in a chunk. - */ -static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code, - u_int8_t identifier, aka_subtype_t type, ...) -{ - chunk_t message = chunk_alloca(512); /* is enought for all current messages */ - chunk_t pos = message; - eap_payload_t *payload; - va_list args; - aka_attribute_t attr; - u_int8_t *mac_pos = NULL; - - /* write EAP header, skip length bytes */ - *pos.ptr++ = code; - *pos.ptr++ = identifier; - pos.ptr += 2; - pos.len -= 4; - /* write AKA header with type and subtype, null reserved bytes */ - *pos.ptr++ = EAP_AKA; - *pos.ptr++ = type; - *pos.ptr++ = 0; - *pos.ptr++ = 0; - pos.len -= 4; - - va_start(args, type); - while ((attr = va_arg(args, aka_attribute_t)) != AT_END) - { - chunk_t data = va_arg(args, chunk_t); - - DBG3(DBG_IKE, "building %N %B", aka_attribute_names, attr, &data); - - /* write attribute header */ - *pos.ptr++ = attr; - pos.len--; - - switch (attr) - { - case AT_RES: - { - /* attribute length in 4byte words */ - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - /* RES length in bits */ - *(u_int16_t*)pos.ptr = htons(data.len * 8); - pos = chunk_skip(pos, sizeof(u_int16_t)); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_AUTN: - case AT_RAND: - { - *pos.ptr++ = data.len/4 + 1; pos.len--; - *pos.ptr++ = 0; pos.len--; - *pos.ptr++ = 0; pos.len--; - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_MAC: - { - *pos.ptr++ = 5; pos.len--; - *pos.ptr++ = 0; pos.len--; - *pos.ptr++ = 0; pos.len--; - mac_pos = pos.ptr; - /* MAC is calculated over message including zeroed AT_MAC attribute */ - memset(mac_pos, 0, AT_MAC_LENGTH); - pos.ptr += AT_MAC_LENGTH; - pos.len -= AT_MAC_LENGTH; - break; - } - default: - { - /* length is data length in 4-bytes + 1 for header */ - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - } - } - } - va_end(args); - - /* calculate message length, write into header */ - message.len = pos.ptr - message.ptr; - *(u_int16_t*)(message.ptr + 2) = htons(message.len); - - /* create MAC if AT_MAC attribte was included */ - if (mac_pos) - { - this->signer->set_key(this->signer, this->k_auth); - DBG3(DBG_IKE, "AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - this->signer->get_signature(this->signer, message, mac_pos); - DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LENGTH); - } - - /* payload constructor takes data with some bytes skipped */ - payload = eap_payload_create_data(message); - - DBG3(DBG_IKE, "created EAP message %B", &message); - return payload; -} - -/** - * generate a new non-zero identifier - */ -static u_char get_identifier() -{ - u_char id; - - do { - id = random(); - } while (!id); - return id; -} - -/** - * Initiate a AKA-Challenge using SQN - */ -static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn, - eap_payload_t **out) -{ - rng_t *rng; - chunk_t mac, ak, autn; - - mac = chunk_alloca(MAC_LENGTH); - ak = chunk_alloca(AK_LENGTH); - chunk_free(&this->rand); - chunk_free(&this->xres); - - /* generate RAND: - * we use a registered RNG, not f0() proposed in S.S0055 - */ - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng) - { - DBG1(DBG_IKE, "generating RAND for EAP-AKA authentication failed"); - return FAILED; - } - rng->allocate_bytes(rng, RAND_LENGTH, &this->rand); - rng->destroy(rng); - -# ifdef TEST_VECTORS - /* Test vector for RAND */ - u_int8_t test_rand[] = { - 0x4b,0x05,0x2b,0x20,0xe2,0xa0,0x6c,0x8f, - 0xf7,0x00,0xda,0x51,0x2b,0x4e,0x11,0x1e, - }; - memcpy(this->rand.ptr, test_rand, this->rand.len); -# endif /* TEST_VECTORS */ - - /* Get the shared key K: */ - if (load_key(this->server, this->peer, &this->k) != SUCCESS) - { - DBG1(DBG_IKE, "no shared key found for IDs '%Y' - '%Y' to authenticate " - "with EAP-AKA", this->server, this->peer); - return FAILED; - } - -# ifdef TEST_VECTORS - /* Test vector for K */ - u_int8_t test_k[] = { - 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, - 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, - }; - memcpy(this->k.ptr, test_k, this->k.len); -# endif /* TEST_VECTORS */ - - /* generate MAC */ - f1(this, this->k, this->rand, sqn, amf, mac.ptr); - - /* generate AK */ - f5(this, this->k, this->rand, ak.ptr); - - /* precalculate XRES as expected from client */ - this->xres = chunk_alloc(RES_LENGTH); - f2(this, this->k, this->rand, this->xres.ptr); - - /* calculate AUTN = (SQN xor AK) || AMF || MAC */ - autn = chunk_cata("ccc", sqn, amf, mac); - memxor(autn.ptr, ak.ptr, ak.len); - DBG3(DBG_IKE, "AUTN %B", &autn); - - - /* derive K_encr, K_auth, MSK, EMSK */ - derive_keys(this, this->peer); - - /* build payload */ - *out = build_aka_payload(this, EAP_REQUEST, get_identifier(), AKA_CHALLENGE, - AT_RAND, this->rand, AT_AUTN, autn, AT_MAC, - chunk_empty, AT_END); - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.initiate for an EAP_AKA server - */ -static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out) -{ - chunk_t sqn = chunk_alloca(SQN_LENGTH); - - /* we use an offset of 3 minutes to tolerate clock inaccuracy - * without the need to synchronize sequence numbers */ - update_sqn(sqn.ptr, 180); - -# ifdef TEST_VECTORS - /* Test vector for SQN */ - u_int8_t test_sqn[] = {0x00,0x00,0x00,0x00,0x00,0x01}; - memcpy(sqn.ptr, test_sqn, sqn.len); -# endif /* TEST_VECTORS */ - - return server_initiate_challenge(this, sqn, out); -} - -static status_t server_process_synchronize(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t attr, auts = chunk_empty, pos, message, macs, xmacs, sqn, aks, amf; - u_int i; - - message = in->get_data(in); - pos = message; - read_header(&pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_AUTS: - auts = attr; - continue; - default: - if (attribute >= 0 && attribute <= 127) - { - DBG1(DBG_IKE, "found non skippable attribute %N", - aka_attribute_names, attribute); - return FAILED; - } - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - continue; - } - break; - } - - if (auts.len != AUTS_LENGTH) - { - DBG1(DBG_IKE, "synchronization request didn't contain useable AUTS"); - return FAILED; - } - - chunk_split(auts, "mm", SQN_LENGTH, &sqn, MAC_LENGTH, &macs); - aks = chunk_alloca(AK_LENGTH); - f5star(this, this->k, this->rand, aks.ptr); - /* decrypt serial number by XORing AKS */ - memxor(sqn.ptr, aks.ptr, aks.len); - - /* verify MACS */ - xmacs = chunk_alloca(MAC_LENGTH); - amf = chunk_alloca(AMF_LENGTH); - /* an AMF of zero is used for MACS calculation */ - memset(amf.ptr, 0, amf.len); - f1star(this, this->k, this->rand, sqn, amf, xmacs.ptr); - if (!chunk_equals(macs, xmacs)) - { - DBG1(DBG_IKE, "received MACS does not match XMACS"); - DBG3(DBG_IKE, "MACS %B XMACS %B", &macs, &xmacs); - return FAILED; - } - - /* retry the challenge with the received SQN + 1*/ - for (i = SQN_LENGTH - 1; i >= 0; i--) - { - if (++sqn.ptr[i] != 0) - { - break; - } - } - return server_initiate_challenge(this, sqn, out); -} - -/** - * process an AKA_Challenge response - */ -static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t *in) -{ - chunk_t attr, res = chunk_empty, at_mac = chunk_empty, pos, message; - - message = in->get_data(in); - pos = message; - read_header(&pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_RES: - res = attr; - if (attr.len == 2 + RES_LENGTH && - *(u_int16_t*)attr.ptr == htons(RES_LENGTH * 8)) - { - res = chunk_skip(attr, 2); - } - continue; - - case AT_MAC: - attr = chunk_skip(attr, 2); - at_mac = chunk_clonea(attr); - /* zero MAC in message for MAC verification */ - memset(attr.ptr, 0, attr.len); - continue; - default: - if (attribute >= 0 && attribute <= 127) - { - DBG1(DBG_IKE, "found non skippable attribute %N", - aka_attribute_names, attribute); - return FAILED; - } - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - continue; - } - break; - } - - /* verify EAP message MAC AT_MAC */ - { - this->signer->set_key(this->signer, this->k_auth); - DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - if (!this->signer->verify_signature(this->signer, message, at_mac)) - { - DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed"); - return FAILED; - } - } - - /* compare received RES against stored precalculated XRES */ - if (!chunk_equals(res, this->xres)) - { - DBG1(DBG_IKE, "received RES does not match XRES"); - DBG3(DBG_IKE, "RES %Bb XRES %B", &res, &this->xres); - return FAILED; - } - return SUCCESS; -} - -/** - * Implementation of eap_method_t.process for EAP_AKA servers - */ -static status_t server_process(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message; - aka_subtype_t type; - - message = in->get_data(in); - type = read_header(&message); - - DBG3(DBG_IKE, "received EAP message %B", &message); - - switch (type) - { - case AKA_CHALLENGE: - { - return server_process_challenge(this, in); - } - case AKA_AUTHENTICATION_REJECT: - case AKA_CLIENT_ERROR: - { - DBG1(DBG_IKE, "received %N, authentication failed", - aka_subtype_names, type); - return FAILED; - } - case AKA_SYNCHRONIZATION_FAILURE: - { - DBG1(DBG_IKE, "received %N, retrying with received SQN", - aka_subtype_names, type); - return server_process_synchronize(this, in, out); - } - default: - DBG1(DBG_IKE, "received unknown AKA subtype %N, authentication failed", - aka_subtype_names, type); - return FAILED; - } -} - -/** - * Process an incoming AKA-Challenge client side - */ -static status_t peer_process_challenge(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t attr = chunk_empty; - chunk_t autn = chunk_empty, at_mac = chunk_empty; - chunk_t ak, sqn, sqn_ak, mac, xmac, res, amf, message, pos; - u_int8_t identifier; - - ak = chunk_alloca(AK_LENGTH); - xmac = chunk_alloca(MAC_LENGTH); - res = chunk_alloca(RES_LENGTH); - chunk_free(&this->rand); - - message = in->get_data(in); - pos = message; - read_header(&pos); - identifier = in->get_identifier(in); - - DBG3(DBG_IKE, "reading attributes from %B", &pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_RAND: - this->rand = chunk_clone(chunk_skip(attr, 2)); - continue; - case AT_AUTN: - autn = chunk_skip(attr, 2); - continue; - case AT_MAC: - attr = chunk_skip(attr, 2); - at_mac = chunk_clonea(attr); - /* set MAC in message to zero for own MAC verification */ - memset(attr.ptr, 0, attr.len); - continue; - default: - if (attribute >= 0 && attribute <= 127) - { - /* non skippable attribute, abort */ - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "found non skippable attribute %N, sending %N %d", - aka_attribute_names, attribute, - aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - continue; - } - break; - } - - if (this->rand.len != RAND_LENGTH || autn.len != AUTN_LENGTH) - { - /* required attributes wrong/not found, abort */ - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "could not find valid RAND/AUTN attribute, sending %N %d", - aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - - DBG3(DBG_IKE, "using autn %B", &autn); - /* split up AUTN = SQN xor AK | AMF | MAC */ - chunk_split(autn, "mmm", SQN_LENGTH, &sqn_ak, AMF_LENGTH, &amf, MAC_LENGTH, &mac); - - /* Get the shared key K: */ - chunk_free(&this->k); - if (load_key(this->peer, this->server, &this->k) != SUCCESS) - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_AUTHENTICATION_REJECT, AT_END); - DBG3(DBG_IKE, "no shared key found for IDs '%Y' - '%Y' to authenticate " - "with EAP-AKA, sending %N", this->peer, this->server, - aka_subtype_names, AKA_AUTHENTICATION_REJECT); - return NEED_MORE; - } - DBG3(DBG_IKE, "using K %B", &this->k); -# ifdef TEST_VECTORS - /* Test vector for K */ - u_int8_t test_k[] = { - 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, - 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, - }; - memcpy(this->k.ptr, test_k, this->k.len); -# endif /* TEST_VECTORS */ - - /* calculate anonymity key AK */ - f5(this, this->k, this->rand, ak.ptr); - DBG3(DBG_IKE, "using rand %B", &this->rand); - DBG3(DBG_IKE, "using ak %B", &ak); - /* XOR AK into SQN to decrypt it */ - - sqn = chunk_clonea(sqn_ak); - - DBG3(DBG_IKE, "using ak xor sqn %B", &sqn_ak); - memxor(sqn.ptr, ak.ptr, sqn.len); - DBG3(DBG_IKE, "using sqn %B", &sqn); - - /* calculate expected MAC and compare against received one */ - f1(this, this->k, this->rand, sqn, amf, xmac.ptr); - if (!chunk_equals(mac, xmac)) - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_AUTHENTICATION_REJECT, AT_END); - DBG1(DBG_IKE, "received MAC does not match XMAC, sending %N", - aka_subtype_names, AKA_AUTHENTICATION_REJECT); - DBG3(DBG_IKE, "MAC %B\nXMAC %B", &mac, &xmac); - return NEED_MORE; - } - -#if SEQ_CHECK - if (memcmp(peer_sqn.ptr, sqn.ptr, sqn.len) >= 0) - { - /* sequence number invalid. send AUTS */ - chunk_t auts, macs, aks, amf; - - macs = chunk_alloca(MAC_LENGTH); - aks = chunk_alloca(AK_LENGTH); - amf = chunk_alloca(AMF_LENGTH); - - /* AMF is set to zero in AKA_SYNCHRONIZATION_FAILURE */ - memset(amf.ptr, 0, amf.len); - /* AKS = f5*(RAND) */ - f5star(this, this->k, this->rand, aks.ptr); - /* MACS = f1*(RAND) */ - f1star(this, this->k, this->rand, peer_sqn, amf, macs.ptr); - /* AUTS = SQN xor AKS | MACS */ - memxor(aks.ptr, peer_sqn.ptr, aks.len); - auts = chunk_cata("cc", aks, macs); - - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_SYNCHRONIZATION_FAILURE, - AT_AUTS, auts, AT_END); - DBG1(DBG_IKE, "received SQN invalid, sending %N", - aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); - DBG3(DBG_IKE, "received SQN %B\ncurrent SQN %B", &sqn, &peer_sqn); - return NEED_MORE; - } -#endif /* SEQ_CHECK */ - - /* derive K_encr, K_auth, MSK, EMSK */ - derive_keys(this, this->peer); - - /* verify EAP message MAC AT_MAC */ - DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - this->signer->set_key(this->signer, this->k_auth); - if (!this->signer->verify_signature(this->signer, message, at_mac)) - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "MAC in AT_MAC attribute verification " - "failed, sending %N %d", aka_attribute_names, - AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - - /* update stored SQN to the received one */ - memcpy(peer_sqn.ptr, sqn.ptr, sqn.len); - - /* calculate RES */ - f2(this, this->k, this->rand, res.ptr); - - /* build response */ - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE, - AT_RES, res, AT_MAC, chunk_empty, AT_END); - return NEED_MORE; -} - -/** - * Process an incoming AKA-Notification as client - */ -static status_t peer_process_notification(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, pos, attr; - u_int8_t identifier; - - message = in->get_data(in); - pos = message; - read_header(&pos); - identifier = in->get_identifier(in); - - DBG3(DBG_IKE, "reading attributes from %B", &pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_NOTIFICATION: - { - u_int16_t code; - - if (attr.len != 2) - { - DBG1(DBG_IKE, "received invalid AKA notification, ignored"); - continue; - } - code = ntohs(*(u_int16_t*)attr.ptr); - switch (code) - { - case 0: - DBG1(DBG_IKE, "received AKA notification 'general " - "failure after authentication' (%d)", code); - return FAILED; - case 16384: - DBG1(DBG_IKE, "received AKA notification 'general " - "failure' (%d)", code); - return FAILED; - case 32768: - DBG1(DBG_IKE, "received AKA notification 'successfully " - "authenticated' (%d)", code); - continue; - case 1026: - DBG1(DBG_IKE, "received AKA notification 'access " - "temporarily denied' (%d)", code); - return FAILED; - case 1031: - DBG1(DBG_IKE, "received AKA notification 'not " - "subscribed to service' (%d)", code); - return FAILED; - default: - DBG1(DBG_IKE, "received AKA notification code %d, " - "ignored", code); - continue; - } - } - default: - if (attribute >= 0 && attribute <= 127) - { - DBG1(DBG_IKE, "ignoring non-skippable attribute %N in %N", - aka_attribute_names, attribute, aka_subtype_names, - AKA_NOTIFICATION); - } - else - { - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - } - continue; - } - break; - } - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.process for an EAP_AKA peer - */ -static status_t peer_process(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - aka_subtype_t type; - chunk_t message; - u_int8_t identifier; - - message = in->get_data(in); - type = read_header(&message); - identifier = in->get_identifier(in); - - DBG3(DBG_IKE, "received EAP message %B", &message); - - switch (type) - { - case AKA_CHALLENGE: - { - return peer_process_challenge(this, in, out); - } - case AKA_NOTIFICATION: - { - return peer_process_notification(this, in, out); - } - default: - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "received unsupported %N request, sending %N %d", - aka_subtype_names, type, - aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - } -} - -/** - * Implementation of eap_method_t.initiate for an EAP AKA peer - */ -static status_t peer_initiate(private_eap_aka_t *this, eap_payload_t **out) -{ - /* peer never initiates */ - return FAILED; -} - -/** - * Implementation of eap_method_t.get_type. - */ -static eap_type_t get_type(private_eap_aka_t *this, u_int32_t *vendor) -{ - *vendor = 0; - return EAP_AKA; -} - -/** - * Implementation of eap_method_t.get_msk. - */ -static status_t get_msk(private_eap_aka_t *this, chunk_t *msk) -{ - if (this->msk.ptr) - { - *msk = this->msk; - return SUCCESS; - } - return FAILED; -} - -/** - * Implementation of eap_method_t.is_mutual. - */ -static bool is_mutual(private_eap_aka_t *this) -{ - return TRUE; -} - -/** - * Implementation of eap_method_t.destroy. - */ -static void destroy(private_eap_aka_t *this) -{ - this->server->destroy(this->server); - this->peer->destroy(this->peer); - DESTROY_IF(this->sha1); - DESTROY_IF(this->signer); - DESTROY_IF(this->prf); - DESTROY_IF(this->keyed_prf); - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_free(&this->xres); - chunk_free(&this->k); - chunk_free(&this->rand); - free(this); -} - -/** - * generic constructor used by client & server - */ -static private_eap_aka_t *eap_aka_create_generic(identification_t *server, - identification_t *peer) -{ - private_eap_aka_t *this = malloc_thing(private_eap_aka_t); - - this->public.eap_method_interface.initiate = NULL; - this->public.eap_method_interface.process = NULL; - this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; - this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; - this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; - this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; - - /* private data */ - this->server = server->clone(server); - this->peer = peer->clone(peer); - this->k_encr = chunk_empty; - this->k_auth = chunk_empty; - this->msk = chunk_empty; - this->emsk = chunk_empty; - this->xres = chunk_empty; - this->k = chunk_empty; - this->rand = chunk_empty; - - this->sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); - this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_SHA1_128); - this->prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160); - this->keyed_prf = lib->crypto->create_prf(lib->crypto, PRF_KEYED_SHA1); - - if (!this->sha1 || !this->signer || !this->prf || !this->keyed_prf) - { - DBG1(DBG_IKE, "unable to initiate EAP-AKA, FIPS-PRF/SHA1 not supported"); - DESTROY_IF(this->sha1); - DESTROY_IF(this->signer); - DESTROY_IF(this->prf); - DESTROY_IF(this->keyed_prf); - destroy(this); - return NULL; - } - return this; -} - -/* - * Described in header. - */ -eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer) -{ - private_eap_aka_t *this = eap_aka_create_generic(server, peer); - - if (this) - { - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process; - } - return (eap_aka_t*)this; -} - -/* - * Described in header. - */ -eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer) -{ - private_eap_aka_t *this = eap_aka_create_generic(server, peer); - - if (this) - { - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process; - } - return (eap_aka_t*)this; -} - diff --git a/src/charon/plugins/eap_aka/eap_aka.h b/src/charon/plugins/eap_aka/eap_aka.h deleted file mode 100644 index 7686802cf..000000000 --- a/src/charon/plugins/eap_aka/eap_aka.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2008 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -/** - * @defgroup eap_aka_i eap_aka - * @{ @ingroup eap_aka - */ - -#ifndef EAP_AKA_H_ -#define EAP_AKA_H_ - -typedef struct eap_aka_t eap_aka_t; - -#include <sa/authenticators/eap/eap_method.h> - -/** check SEQ values as client for validity, disabled by default */ -#ifndef SEQ_CHECK -# define SEQ_CHECK 0 -#endif - -/** - * Implementation of the eap_method_t interface using EAP-AKA. - * - * EAP-AKA uses 3rd generation mobile phone standard authentication - * mechanism for authentication. It is a mutual authentication - * mechanism which establishs a shared key and therefore supports EAP_ONLY - * authentication. This implementation follows the standard of the - * 3GPP2 (S.S0055) and not the one of 3GGP. - * The shared key used for authentication is from ipsec.secrets. The - * peers ID is used to query it. - * The AKA mechanism uses sequence numbers to detect replay attacks. The - * peer stores the sequence number normally in a USIM and accepts - * incremental sequence numbers (incremental for lifetime of the USIM). To - * prevent a complex sequence number management, this implementation uses - * a sequence number derived from time. It is initialized to the startup - * time of the daemon. As long as the (UTC) time of the system is not - * turned back while the daemon is not running, this method is secure. - * To enable time based SEQs, define SEQ_CHECK as 1. Default is to accept - * any SEQ numbers. This allows an attacker to do replay attacks. But since - * the server has proven his identity via IKE, such an attack is only - * possible between server and AAA (if any). - */ -struct eap_aka_t { - - /** - * Implemented eap_method_t interface. - */ - eap_method_t eap_method_interface; -}; - -/** - * Creates the server implementation of the EAP method EAP-AKA. - * - * @param server ID of the EAP server - * @param peer ID of the EAP client - * @return eap_aka_t object - */ -eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer); - -/** - * Creates the peer implementation of the EAP method EAP-AKA. - * - * @param server ID of the EAP server - * @param peer ID of the EAP client - * @return eap_aka_t object - */ -eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer); - -#endif /** EAP_AKA_H_ @}*/ diff --git a/src/charon/plugins/eap_aka/eap_aka_peer.c b/src/charon/plugins/eap_aka/eap_aka_peer.c new file mode 100644 index 000000000..26546809d --- /dev/null +++ b/src/charon/plugins/eap_aka/eap_aka_peer.c @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2006-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_aka_peer.h" + +#include <library.h> +#include <daemon.h> + +#include <simaka_message.h> +#include <simaka_crypto.h> + +typedef struct private_eap_aka_peer_t private_eap_aka_peer_t; + +/** + * Private data of an eap_aka_peer_t object. + */ +struct private_eap_aka_peer_t { + + /** + * Public authenticator_t interface. + */ + eap_aka_peer_t public; + + /** + * EAP-AKA crypto helper + */ + simaka_crypto_t *crypto; + + /** + * permanent ID of peer + */ + identification_t *permanent; + + /** + * Pseudonym identity the peer uses + */ + identification_t *pseudonym; + + /** + * Reauthentication identity the peer uses + */ + identification_t *reauth; + + /** + * MSK + */ + chunk_t msk; + + /** + * Master key, if reauthentication is used + */ + char mk[HASH_SIZE_SHA1]; + + /** + * Counter value if reauthentication is used + */ + u_int16_t counter; +}; + +/** + * Create a AKA_CLIENT_ERROR: "Unable to process" + */ +static eap_payload_t* create_client_error(private_eap_aka_peer_t *this, + u_int8_t identifier) +{ + simaka_message_t *message; + eap_payload_t *out; + u_int16_t encoded; + + DBG1(DBG_IKE, "sending client error '%N'", + simaka_client_error_names, AKA_UNABLE_TO_PROCESS); + + message = simaka_message_create(FALSE, identifier, EAP_AKA, + AKA_CLIENT_ERROR, this->crypto); + encoded = htons(AKA_UNABLE_TO_PROCESS); + message->add_attribute(message, AT_CLIENT_ERROR_CODE, + chunk_create((char*)&encoded, sizeof(encoded))); + out = message->generate(message, chunk_empty); + message->destroy(message); + return out; +} + +/** + * process an EAP-AKA/Request/Identity message + */ +static status_t process_identity(private_eap_aka_peer_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + simaka_message_t *message; + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, id = chunk_empty; + simaka_attribute_t id_req = 0; + + /* reset previously uses reauthentication/pseudonym data */ + this->crypto->clear_keys(this->crypto); + DESTROY_IF(this->pseudonym); + this->pseudonym = NULL; + DESTROY_IF(this->reauth); + this->reauth = NULL; + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_ANY_ID_REQ: + case AT_FULLAUTH_ID_REQ: + case AT_PERMANENT_ID_REQ: + id_req = type; + break; + default: + if (!simaka_attribute_skippable(type)) + { + *out = create_client_error(this, in->get_identifier(in)); + enumerator->destroy(enumerator); + return NEED_MORE; + } + break; + } + } + enumerator->destroy(enumerator); + + switch (id_req) + { + case AT_ANY_ID_REQ: + this->reauth = charon->sim->card_get_reauth(charon->sim, + this->permanent, this->mk, &this->counter); + if (this->reauth) + { + id = this->reauth->get_encoding(this->reauth); + break; + } + /* FALL */ + case AT_FULLAUTH_ID_REQ: + this->pseudonym = charon->sim->card_get_pseudonym(charon->sim, + this->permanent); + if (this->pseudonym) + { + id = this->pseudonym->get_encoding(this->pseudonym); + break; + } + /* FALL */ + case AT_PERMANENT_ID_REQ: + id = this->permanent->get_encoding(this->permanent); + break; + default: + break; + } + message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA, + AKA_IDENTITY, this->crypto); + if (id.len) + { + message->add_attribute(message, AT_IDENTITY, id); + } + *out = message->generate(message, chunk_empty); + message->destroy(message); + + return NEED_MORE; +} + +/** + * Process an EAP-AKA/Request/Challenge message + */ +static status_t process_challenge(private_eap_aka_peer_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + simaka_message_t *message; + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, rand = chunk_empty, autn = chunk_empty, mk; + u_char res[AKA_RES_MAX], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN]; + int res_len; + identification_t *id; + status_t status; + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_RAND: + rand = data; + break; + case AT_AUTN: + autn = data; + break; + default: + if (!simaka_attribute_skippable(type)) + { + *out = create_client_error(this, in->get_identifier(in)); + enumerator->destroy(enumerator); + return NEED_MORE; + } + break; + } + } + enumerator->destroy(enumerator); + + if (!rand.len || !autn.len) + { + DBG1(DBG_IKE, "received invalid EAP-AKA challenge message"); + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + + status = charon->sim->card_get_quintuplet(charon->sim, this->permanent, + rand.ptr, autn.ptr, ck, ik, res, &res_len); + if (status == INVALID_STATE && + charon->sim->card_resync(charon->sim, this->permanent, rand.ptr, auts)) + { + DBG1(DBG_IKE, "received SQN invalid, sending %N", + simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); + message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA, + AKA_SYNCHRONIZATION_FAILURE, this->crypto); + message->add_attribute(message, AT_AUTS, + chunk_create(auts, AKA_AUTS_LEN)); + *out = message->generate(message, chunk_empty); + message->destroy(message); + return NEED_MORE; + } + if (status != SUCCESS) + { + DBG1(DBG_IKE, "no USIM found with quintuplets for '%Y', sending %N", + this->permanent, simaka_subtype_names, AKA_AUTHENTICATION_REJECT); + message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA, + AKA_AUTHENTICATION_REJECT, this->crypto); + *out = message->generate(message, chunk_empty); + message->destroy(message); + return NEED_MORE; + } + + id = this->permanent; + if (this->pseudonym) + { + id = this->pseudonym; + } + data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN), + chunk_create(ck, AKA_CK_LEN)); + free(this->msk.ptr); + this->msk = this->crypto->derive_keys_full(this->crypto, id, data, &mk); + memcpy(this->mk, mk.ptr, mk.len); + free(mk.ptr); + + /* Verify AT_MAC attribute and parse() again after key derivation, + * reading encrypted attributes */ + if (!in->verify(in, chunk_empty) || !in->parse(in)) + { + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_NEXT_REAUTH_ID: + this->counter = 0; + id = identification_create_from_data(data); + charon->sim->card_set_reauth(charon->sim, this->permanent, id, + this->mk, this->counter); + id->destroy(id); + break; + case AT_NEXT_PSEUDONYM: + id = identification_create_from_data(data); + charon->sim->card_set_pseudonym(charon->sim, this->permanent, id); + id->destroy(id); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA, + AKA_CHALLENGE, this->crypto); + message->add_attribute(message, AT_RES, chunk_create(res, res_len)); + *out = message->generate(message, chunk_empty); + message->destroy(message); + return NEED_MORE; +} + +/** + * Check if a received counter value is acceptable + */ +static bool counter_too_small(private_eap_aka_peer_t *this, chunk_t chunk) +{ + u_int16_t counter; + + memcpy(&counter, chunk.ptr, sizeof(counter)); + counter = htons(counter); + return counter < this->counter; +} + +/** + * process an EAP-AKA/Request/Reauthentication message + */ +static status_t process_reauthentication(private_eap_aka_peer_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + simaka_message_t *message; + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty; + + if (!this->reauth) + { + DBG1(DBG_IKE, "received %N, but not expected", + simaka_subtype_names, AKA_REAUTHENTICATION); + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + + this->crypto->derive_keys_reauth(this->crypto, + chunk_create(this->mk, HASH_SIZE_SHA1)); + + /* verify MAC and parse again with decryption key */ + if (!in->verify(in, chunk_empty) || !in->parse(in)) + { + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_COUNTER: + counter = data; + break; + case AT_NONCE_S: + nonce = data; + break; + case AT_NEXT_REAUTH_ID: + id = data; + break; + default: + if (!simaka_attribute_skippable(type)) + { + *out = create_client_error(this, in->get_identifier(in)); + enumerator->destroy(enumerator); + return NEED_MORE; + } + break; + } + } + enumerator->destroy(enumerator); + + if (!nonce.len || !counter.len) + { + DBG1(DBG_IKE, "EAP-AKA/Request/Reauthentication message incomplete"); + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + + message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA, + AKA_REAUTHENTICATION, this->crypto); + if (counter_too_small(this, counter)) + { + DBG1(DBG_IKE, "reauthentication counter too small"); + message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty); + } + else + { + free(this->msk.ptr); + this->msk = this->crypto->derive_keys_reauth_msk(this->crypto, + this->reauth, counter, nonce, + chunk_create(this->mk, HASH_SIZE_SHA1)); + if (id.len) + { + identification_t *reauth; + + reauth = identification_create_from_data(data); + charon->sim->card_set_reauth(charon->sim, this->permanent, reauth, + this->mk, this->counter); + reauth->destroy(reauth); + } + } + message->add_attribute(message, AT_COUNTER, counter); + *out = message->generate(message, nonce); + message->destroy(message); + return NEED_MORE; +} + +/** + * Process an EAP-AKA/Request/Notification message + */ +static status_t process_notification(private_eap_aka_peer_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + simaka_message_t *message; + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data; + bool success = TRUE; + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == AT_NOTIFICATION) + { + u_int16_t code; + + memcpy(&code, data.ptr, sizeof(code)); + code = ntohs(code); + + /* test success bit */ + if (!(data.ptr[0] & 0x80)) + { + success = FALSE; + DBG1(DBG_IKE, "received EAP-AKA notification error '%N'", + simaka_notification_names, code); + } + else + { + DBG1(DBG_IKE, "received EAP-AKA notification '%N'", + simaka_notification_names, code); + } + } + else if (!simaka_attribute_skippable(type)) + { + success = FALSE; + break; + } + } + enumerator->destroy(enumerator); + + if (success) + { /* empty notification reply */ + message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA, + AKA_NOTIFICATION, this->crypto); + *out = message->generate(message, chunk_empty); + message->destroy(message); + } + else + { + *out = create_client_error(this, in->get_identifier(in)); + } + return NEED_MORE; +} + + +/** + * Implementation of eap_method_t.process + */ +static status_t process(private_eap_aka_peer_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + simaka_message_t *message; + status_t status; + + message = simaka_message_create_from_payload(in, this->crypto); + if (!message) + { + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + if (!message->parse(message)) + { + message->destroy(message); + *out = create_client_error(this, in->get_identifier(in)); + return NEED_MORE; + } + switch (message->get_subtype(message)) + { + case AKA_IDENTITY: + status = process_identity(this, message, out); + break; + case AKA_CHALLENGE: + status = process_challenge(this, message, out); + break; + case AKA_REAUTHENTICATION: + status = process_reauthentication(this, message, out); + break; + case AKA_NOTIFICATION: + status = process_notification(this, message, out); + break; + default: + DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N", + simaka_subtype_names, message->get_subtype(message)); + *out = create_client_error(this, in->get_identifier(in)); + status = NEED_MORE; + break; + } + message->destroy(message); + return status; +} + +/** + * Implementation of eap_method_t.initiate + */ +static status_t initiate(private_eap_aka_peer_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_aka_peer_t *this, u_int32_t *vendor) +{ + *vendor = 0; + return EAP_AKA; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_aka_peer_t *this, chunk_t *msk) +{ + if (this->msk.ptr) + { + *msk = this->msk; + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_aka_peer_t *this) +{ + return TRUE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_aka_peer_t *this) +{ + this->crypto->destroy(this->crypto); + this->permanent->destroy(this->permanent); + DESTROY_IF(this->pseudonym); + DESTROY_IF(this->reauth); + free(this->msk.ptr); + free(this); +} + +/* + * Described in header. + */ +eap_aka_peer_t *eap_aka_peer_create(identification_t *server, + identification_t *peer) +{ + private_eap_aka_peer_t *this = malloc_thing(private_eap_aka_peer_t); + + this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; + this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; + this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; + this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.interface.destroy = (void(*)(eap_method_t*))destroy; + + this->crypto = simaka_crypto_create(); + if (!this->crypto) + { + free(this); + return NULL; + } + this->permanent = peer->clone(peer); + this->pseudonym = NULL; + this->reauth = NULL; + this->msk = chunk_empty; + + return &this->public; +} + diff --git a/src/charon/plugins/eap_aka/eap_aka_peer.h b/src/charon/plugins/eap_aka/eap_aka_peer.h new file mode 100644 index 000000000..65a210406 --- /dev/null +++ b/src/charon/plugins/eap_aka/eap_aka_peer.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup eap_aka_peer eap_aka_peer + * @{ @ingroup eap_aka + */ + +#ifndef EAP_AKA_PEER_H_ +#define EAP_AKA_PEER_H_ + +typedef struct eap_aka_peer_t eap_aka_peer_t; + +#include <sa/authenticators/eap/eap_method.h> + +/** + * Implementation of the eap_method_t interface using EAP-AKA as a client. + */ +struct eap_aka_peer_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t interface; +}; + +/** + * Creates the peer implementation of the EAP method EAP-AKA. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_aka_peer_t object + */ +eap_aka_peer_t *eap_aka_peer_create(identification_t *server, + identification_t *peer); + +#endif /** EAP_AKA_PEER_H_ @}*/ diff --git a/src/charon/plugins/eap_aka/eap_aka_plugin.c b/src/charon/plugins/eap_aka/eap_aka_plugin.c index e4a5326fe..c44a08966 100644 --- a/src/charon/plugins/eap_aka/eap_aka_plugin.c +++ b/src/charon/plugins/eap_aka/eap_aka_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Martin Willi + * Copyright (C) 2008-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -15,7 +15,8 @@ #include "eap_aka_plugin.h" -#include "eap_aka.h" +#include "eap_aka_peer.h" +#include "eap_aka_server.h" #include <daemon.h> @@ -25,9 +26,9 @@ static void destroy(eap_aka_plugin_t *this) { charon->eap->remove_method(charon->eap, - (eap_constructor_t)eap_aka_create_server); + (eap_constructor_t)eap_aka_server_create); charon->eap->remove_method(charon->eap, - (eap_constructor_t)eap_aka_create_peer); + (eap_constructor_t)eap_aka_peer_create); free(this); } @@ -37,14 +38,14 @@ static void destroy(eap_aka_plugin_t *this) plugin_t *plugin_create() { eap_aka_plugin_t *this = malloc_thing(eap_aka_plugin_t); - + this->plugin.destroy = (void(*)(plugin_t*))destroy; - + charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_SERVER, - (eap_constructor_t)eap_aka_create_server); + (eap_constructor_t)eap_aka_server_create); charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_PEER, - (eap_constructor_t)eap_aka_create_peer); - + (eap_constructor_t)eap_aka_peer_create); + return &this->plugin; } diff --git a/src/charon/plugins/eap_aka/eap_aka_plugin.h b/src/charon/plugins/eap_aka/eap_aka_plugin.h index 2c086ca80..938e5ecbd 100644 --- a/src/charon/plugins/eap_aka/eap_aka_plugin.h +++ b/src/charon/plugins/eap_aka/eap_aka_plugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Martin Willi + * Copyright (C) 2008-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -29,7 +29,10 @@ typedef struct eap_aka_plugin_t eap_aka_plugin_t; /** - * EAP-AKA plugin + * EAP-AKA plugin. + * + * EAP-AKA uses 3rd generation mobile phone standard authentication + * mechanism for authentication, as defined RFC4187. */ struct eap_aka_plugin_t { diff --git a/src/charon/plugins/eap_aka/eap_aka_server.c b/src/charon/plugins/eap_aka/eap_aka_server.c new file mode 100644 index 000000000..9baff3e23 --- /dev/null +++ b/src/charon/plugins/eap_aka/eap_aka_server.c @@ -0,0 +1,700 @@ +/* + * Copyright (C) 2006-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_aka_server.h" + +#include <daemon.h> +#include <library.h> + +#include <simaka_message.h> +#include <simaka_crypto.h> + +/** length of the AT_NONCE_S value */ +#define NONCE_LEN 16 + +typedef struct private_eap_aka_server_t private_eap_aka_server_t; + +/** + * Private data of an eap_aka_server_t object. + */ +struct private_eap_aka_server_t { + + /** + * Public authenticator_t interface. + */ + eap_aka_server_t public; + + /** + * EAP-AKA crypto helper + */ + simaka_crypto_t *crypto; + + /** + * permanent ID of the peer + */ + identification_t *permanent; + + /** + * pseudonym ID of peer + */ + identification_t *pseudonym; + + /** + * reauthentication ID of peer + */ + identification_t *reauth; + + /** + * EAP identifier value + */ + u_int8_t identifier; + + /** + * Expected Result XRES + */ + chunk_t xres; + + /** + * Random value RAND + */ + chunk_t rand; + + /** + * MSK + */ + chunk_t msk; + + /** + * Nonce value used in AT_NONCE_S + */ + chunk_t nonce; + + /** + * Counter value negotiated, network order + */ + chunk_t counter; + + /** + * Do we request fast reauthentication? + */ + bool use_reauth; + + /** + * Do we request pseudonym identities? + */ + bool use_pseudonym; + + /** + * Do we request permanent identities? + */ + bool use_permanent; + + /** + * EAP-AKA message we have initiated + */ + simaka_subtype_t pending; + + /** + * Did the client send a synchronize request? + */ + bool synchronized; +}; + +/** + * Create EAP-AKA/Request/Identity message + */ +static status_t identity(private_eap_aka_server_t *this, eap_payload_t **out) +{ + simaka_message_t *message; + + message = simaka_message_create(TRUE, this->identifier++, EAP_AKA, + AKA_IDENTITY, this->crypto); + if (this->use_reauth) + { + message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty); + } + else if (this->use_pseudonym) + { + message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty); + } + else if (this->use_permanent) + { + message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty); + } + *out = message->generate(message, chunk_empty); + message->destroy(message); + + this->pending = AKA_IDENTITY; + return NEED_MORE; +} + +/** + * Create EAP-AKA/Request/Challenge message + */ +static status_t challenge(private_eap_aka_server_t *this, eap_payload_t **out) +{ + simaka_message_t *message; + char rand[AKA_RAND_LEN], xres[AKA_RES_MAX]; + char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN]; + int xres_len; + chunk_t data, mk; + identification_t *id; + + if (!charon->sim->provider_get_quintuplet(charon->sim, this->permanent, + rand, xres, &xres_len, ck, ik, autn)) + { + if (this->use_pseudonym) + { + /* probably received a pseudonym/reauth id we couldn't map */ + DBG1(DBG_IKE, "failed to map pseudonym/reauth identity '%Y', " + "fallback to permanent identity request", this->permanent); + this->use_pseudonym = FALSE; + DESTROY_IF(this->pseudonym); + this->pseudonym = NULL; + return identity(this, out); + } + return FAILED; + } + id = this->permanent; + if (this->pseudonym) + { + id = this->pseudonym; + } + data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN), + chunk_create(ck, AKA_CK_LEN)); + free(this->msk.ptr); + this->msk = this->crypto->derive_keys_full(this->crypto, id, data, &mk); + this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN)); + this->xres = chunk_clone(chunk_create(xres, xres_len)); + + message = simaka_message_create(TRUE, this->identifier++, EAP_AKA, + AKA_CHALLENGE, this->crypto); + message->add_attribute(message, AT_RAND, this->rand); + message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN)); + id = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk.ptr); + if (id) + { + message->add_attribute(message, AT_NEXT_REAUTH_ID, + id->get_encoding(id)); + id->destroy(id); + } + else + { + id = charon->sim->provider_gen_pseudonym(charon->sim, this->permanent); + if (id) + { + message->add_attribute(message, AT_NEXT_PSEUDONYM, + id->get_encoding(id)); + id->destroy(id); + } + } + *out = message->generate(message, chunk_empty); + message->destroy(message); + + free(mk.ptr); + this->pending = AKA_CHALLENGE; + return NEED_MORE; +} + +/** + * Initiate EAP-AKA/Request/Re-authentication message + */ +static status_t reauthenticate(private_eap_aka_server_t *this, + char mk[HASH_SIZE_SHA1], u_int16_t counter, + eap_payload_t **out) +{ + simaka_message_t *message; + identification_t *next; + chunk_t mkc; + rng_t *rng; + + DBG1(DBG_IKE, "initiating EAP-AKA reauthentication"); + + rng = this->crypto->get_rng(this->crypto); + rng->allocate_bytes(rng, NONCE_LEN, &this->nonce); + + mkc = chunk_create(mk, HASH_SIZE_SHA1); + counter = htons(counter); + this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter))); + + this->crypto->derive_keys_reauth(this->crypto, mkc); + this->msk = this->crypto->derive_keys_reauth_msk(this->crypto, + this->reauth, this->counter, this->nonce, mkc); + + message = simaka_message_create(TRUE, this->identifier++, EAP_AKA, + AKA_REAUTHENTICATION, this->crypto); + message->add_attribute(message, AT_COUNTER, this->counter); + message->add_attribute(message, AT_NONCE_S, this->nonce); + next = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk); + if (next) + { + message->add_attribute(message, AT_NEXT_REAUTH_ID, + next->get_encoding(next)); + next->destroy(next); + } + *out = message->generate(message, chunk_empty); + message->destroy(message); + + this->pending = SIM_REAUTHENTICATION; + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.initiate + */ +static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out) +{ + if (this->use_permanent || this->use_pseudonym || this->use_reauth) + { + return identity(this, out); + } + return challenge(this, out); +} + +/** + * Process EAP-AKA/Response/Identity message + */ +static status_t process_identity(private_eap_aka_server_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + identification_t *permanent, *id; + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, identity = chunk_empty; + + if (this->pending != AKA_IDENTITY) + { + DBG1(DBG_IKE, "received %N, but not expected", + simaka_subtype_names, AKA_IDENTITY); + return FAILED; + } + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_IDENTITY: + identity = data; + break; + default: + if (!simaka_attribute_skippable(type)) + { + enumerator->destroy(enumerator); + return FAILED; + } + break; + } + } + enumerator->destroy(enumerator); + + if (!identity.len) + { + DBG1(DBG_IKE, "received incomplete Identity response"); + return FAILED; + } + + id = identification_create_from_data(identity); + if (this->use_reauth) + { + char mk[HASH_SIZE_SHA1]; + u_int16_t counter; + + permanent = charon->sim->provider_is_reauth(charon->sim, id, + mk, &counter); + if (permanent) + { + this->permanent->destroy(this->permanent); + this->permanent = permanent; + this->reauth = id; + return reauthenticate(this, mk, counter, out); + } + /* unable to map, maybe a pseudonym? */ + DBG1(DBG_IKE, "'%Y' is not a reauth identity", id); + this->use_reauth = FALSE; + } + if (this->use_pseudonym) + { + permanent = charon->sim->provider_is_pseudonym(charon->sim, id); + if (permanent) + { + this->permanent->destroy(this->permanent); + this->permanent = permanent; + this->pseudonym = id->clone(id); + /* we already have a new permanent identity now */ + this->use_permanent = FALSE; + } + else + { + DBG1(DBG_IKE, "'%Y' is not a pseudonym", id); + } + } + if (!this->pseudonym && this->use_permanent) + { + /* got a permanent identity or a pseudonym reauth id wou couldn't map, + * try to get quintuplets */ + DBG1(DBG_IKE, "received identity '%Y'", id); + this->permanent->destroy(this->permanent); + this->permanent = id->clone(id); + } + id->destroy(id); + + return challenge(this, out); +} + +/** + * Process EAP-AKA/Response/Challenge message + */ +static status_t process_challenge(private_eap_aka_server_t *this, + simaka_message_t *in) +{ + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, res = chunk_empty; + + if (this->pending != AKA_CHALLENGE) + { + DBG1(DBG_IKE, "received %N, but not expected", + simaka_subtype_names, AKA_CHALLENGE); + return FAILED; + } + /* verify MAC of EAP message, AT_MAC */ + if (!in->verify(in, chunk_empty)) + { + return FAILED; + } + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_RES: + res = data; + break; + default: + if (!simaka_attribute_skippable(type)) + { + enumerator->destroy(enumerator); + return FAILED; + } + break; + } + } + enumerator->destroy(enumerator); + + /* compare received RES against stored XRES */ + if (!chunk_equals(res, this->xres)) + { + DBG1(DBG_IKE, "received RES does not match XRES"); + return FAILED; + } + return SUCCESS; +} + +/** + * process an EAP-AKA/Response/Reauthentication message + */ +static status_t process_reauthentication(private_eap_aka_server_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, counter = chunk_empty; + bool too_small = FALSE; + + if (this->pending != AKA_REAUTHENTICATION) + { + DBG1(DBG_IKE, "received %N, but not expected", + simaka_subtype_names, AKA_REAUTHENTICATION); + return FAILED; + } + /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */ + if (!in->verify(in, this->nonce)) + { + return FAILED; + } + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_COUNTER: + counter = data; + break; + case AT_COUNTER_TOO_SMALL: + too_small = TRUE; + break; + default: + if (!simaka_attribute_skippable(type)) + { + enumerator->destroy(enumerator); + return FAILED; + } + break; + } + } + enumerator->destroy(enumerator); + + if (too_small) + { + DBG1(DBG_IKE, "received %N, initiating full authentication", + simaka_attribute_names, AT_COUNTER_TOO_SMALL); + this->use_reauth = FALSE; + this->crypto->clear_keys(this->crypto); + return challenge(this, out); + } + if (!chunk_equals(counter, this->counter)) + { + DBG1(DBG_IKE, "received counter does not match"); + return FAILED; + } + return SUCCESS; +} + +/** + * Process EAP-AKA/Response/SynchronizationFailure message + */ +static status_t process_synchronize(private_eap_aka_server_t *this, + simaka_message_t *in, eap_payload_t **out) +{ + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data, auts = chunk_empty; + + if (this->synchronized) + { + DBG1(DBG_IKE, "received %N, but peer did already resynchronize", + simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); + return FAILED; + } + + DBG1(DBG_IKE, "received synchronization request, retrying..."); + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + switch (type) + { + case AT_AUTS: + auts = data; + break; + default: + if (!simaka_attribute_skippable(type)) + { + enumerator->destroy(enumerator); + return FAILED; + } + break; + } + } + enumerator->destroy(enumerator); + + if (!auts.len) + { + DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS"); + return FAILED; + } + + if (!charon->sim->provider_resync(charon->sim, this->permanent, + this->rand.ptr, auts.ptr)) + { + DBG1(DBG_IKE, "no AKA provider found supporting " + "resynchronization for '%Y'", this->permanent); + return FAILED; + } + this->synchronized = TRUE; + return challenge(this, out); +} + +/** + * Process EAP-AKA/Response/ClientErrorCode message + */ +static status_t process_client_error(private_eap_aka_server_t *this, + simaka_message_t *in) +{ + enumerator_t *enumerator; + simaka_attribute_t type; + chunk_t data; + + enumerator = in->create_attribute_enumerator(in); + while (enumerator->enumerate(enumerator, &type, &data)) + { + if (type == AT_CLIENT_ERROR_CODE) + { + u_int16_t code; + + memcpy(&code, data.ptr, sizeof(code)); + DBG1(DBG_IKE, "received EAP-AKA client error '%N'", + simaka_client_error_names, ntohs(code)); + } + else if (!simaka_attribute_skippable(type)) + { + break; + } + } + enumerator->destroy(enumerator); + return FAILED; +} + +/** + * Process EAP-AKA/Response/AuthenticationReject message + */ +static status_t process_authentication_reject(private_eap_aka_server_t *this, + simaka_message_t *in) +{ + DBG1(DBG_IKE, "received %N, authentication failed", + simaka_subtype_names, in->get_subtype(in)); + return FAILED; +} + +/** + * Implementation of eap_method_t.process + */ +static status_t process(private_eap_aka_server_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + simaka_message_t *message; + status_t status; + + message = simaka_message_create_from_payload(in, this->crypto); + if (!message) + { + return FAILED; + } + if (!message->parse(message)) + { + message->destroy(message); + return FAILED; + } + switch (message->get_subtype(message)) + { + case AKA_IDENTITY: + status = process_identity(this, message, out); + break; + case AKA_CHALLENGE: + status = process_challenge(this, message); + break; + case AKA_REAUTHENTICATION: + status = process_reauthentication(this, message, out); + break; + case AKA_SYNCHRONIZATION_FAILURE: + status = process_synchronize(this, message, out); + break; + case AKA_CLIENT_ERROR: + status = process_client_error(this, message); + break; + case AKA_AUTHENTICATION_REJECT: + status = process_authentication_reject(this, message); + break; + default: + DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N", + simaka_subtype_names, message->get_subtype(message)); + status = FAILED; + break; + } + message->destroy(message); + return status; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_aka_server_t *this, u_int32_t *vendor) +{ + *vendor = 0; + return EAP_AKA; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_aka_server_t *this, chunk_t *msk) +{ + if (this->msk.ptr) + { + *msk = this->msk; + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_aka_server_t *this) +{ + return TRUE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_aka_server_t *this) +{ + this->crypto->destroy(this->crypto); + this->permanent->destroy(this->permanent); + DESTROY_IF(this->pseudonym); + DESTROY_IF(this->reauth); + free(this->xres.ptr); + free(this->rand.ptr); + free(this->nonce.ptr); + free(this->msk.ptr); + free(this->counter.ptr); + free(this); +} + +/* + * Described in header. + */ +eap_aka_server_t *eap_aka_server_create(identification_t *server, + identification_t *peer) +{ + private_eap_aka_server_t *this = malloc_thing(private_eap_aka_server_t); + + this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; + this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; + this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; + this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.interface.destroy = (void(*)(eap_method_t*))destroy; + + this->crypto = simaka_crypto_create(); + if (!this->crypto) + { + free(this); + return NULL; + } + this->permanent = peer->clone(peer); + this->pseudonym = NULL; + this->reauth = NULL; + this->xres = chunk_empty; + this->rand = chunk_empty; + this->nonce = chunk_empty; + this->msk = chunk_empty; + this->counter = chunk_empty; + this->pending = 0; + this->synchronized = FALSE; + this->use_reauth = this->use_pseudonym = this->use_permanent = + lib->settings->get_bool(lib->settings, + "charon.plugins.eap-aka.request_identity", TRUE); + /* generate a non-zero identifier */ + do { + this->identifier = random(); + } while (!this->identifier); + + return &this->public; +} + diff --git a/src/charon/plugins/eap_aka/eap_aka_server.h b/src/charon/plugins/eap_aka/eap_aka_server.h new file mode 100644 index 000000000..d48fc4c34 --- /dev/null +++ b/src/charon/plugins/eap_aka/eap_aka_server.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup eap_aka_server eap_aka_server + * @{ @ingroup eap_aka + */ + +#ifndef EAP_AKA_SERVER_H_ +#define EAP_AKA_SERVER_H_ + +typedef struct eap_aka_server_t eap_aka_server_t; + +#include <sa/authenticators/eap/eap_method.h> + +/** + * Implementation of the eap_method_t interface using EAP-AKA as server. + */ +struct eap_aka_server_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t interface; +}; + +/** + * Creates the server implementation of the EAP method EAP-AKA. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_aka_server_t object + */ +eap_aka_server_t *eap_aka_server_create(identification_t *server, + identification_t *peer); + +#endif /** EAP_AKA_SERVER_H_ @}*/ |