Checks out moonshot code correctly: Builds with the following
authorPete Fotheringham <pete.fotheringham@codethink.co.uk>
Thu, 29 Dec 2011 09:34:12 +0000 (09:34 +0000)
committerPete Fotheringham <pete.fotheringham@codethink.co.uk>
Thu, 29 Dec 2011 10:59:50 +0000 (10:59 +0000)
$ jhbuild build moonshot
[4] Start shell*** Error during phase configure of moonshot:
$ git apply ~/moonshot/mac-client-installer/0001-Move-moonshot-files-up.patch
$ cp -R moonshot/libeap .
$ ./autogen.sh --prefix /Users/pete/gtk/inst --libdir '/Users/pete/gtk/inst/lib' --enable-acceptor=no --with-krb5=$PREFIX/usr/local
$ ./configure --enable-acceptor=no --with-krb5=$PREFIX/usr/local
$ exit
[2] Ignore error and continue to build

mac-client-installer/0001-Move-moonshot-files-up.patch [new file with mode: 0644]
mac-client-installer/moonshot-mac.modules

diff --git a/mac-client-installer/0001-Move-moonshot-files-up.patch b/mac-client-installer/0001-Move-moonshot-files-up.patch
new file mode 100644 (file)
index 0000000..b1d898a
--- /dev/null
@@ -0,0 +1,25641 @@
+From df22702d72d30843193d98589fee090f2fe63201 Mon Sep 17 00:00:00 2001
+From: Pete Fotheringham <pete.fotheringham@codethink.co.uk>
+Date: Thu, 29 Dec 2011 09:12:51 +0000
+Subject: [PATCH] Move moonshot files up
+
+---
+ Makefile.am                           |    4 +
+ acinclude.m4                          |  364 ++++++++++
+ autogen.sh                            |   16 +
+ build-aux/compile                     |  144 ++++
+ configure.ac                          |   92 +++
+ m4/minuso.m4                          |   35 +
+ mech_eap.spec.in                      |   62 ++
+ mech_eap/.gitignore                   |   32 +
+ mech_eap/AUTHORS                      |    6 +
+ mech_eap/COPYING                      |    3 +
+ mech_eap/LICENSE                      |   31 +
+ mech_eap/Makefile.am                  |  189 ++++++
+ mech_eap/NOTES                        |    9 +
+ mech_eap/README                       |  147 ++++
+ mech_eap/README.samba4                |   52 ++
+ mech_eap/TODO                         |    6 +
+ mech_eap/accept_sec_context.c         | 1072 +++++++++++++++++++++++++++++
+ mech_eap/acquire_cred.c               |   52 ++
+ mech_eap/acquire_cred_with_password.c |   67 ++
+ mech_eap/add_cred.c                   |   87 +++
+ mech_eap/add_cred_with_password.c     |   93 +++
+ mech_eap/authdata_plugin.h            |  331 +++++++++
+ mech_eap/authorize_localname.c        |   54 ++
+ mech_eap/canonicalize_name.c          |   64 ++
+ mech_eap/compare_name.c               |   46 ++
+ mech_eap/context_time.c               |   69 ++
+ mech_eap/delete_name_attribute.c      |   60 ++
+ mech_eap/delete_sec_context.c         |   81 +++
+ mech_eap/dictionary.ukerna            |   20 +
+ mech_eap/display_name.c               |   48 ++
+ mech_eap/display_name_ext.c           |   51 ++
+ mech_eap/display_status.c             |  203 ++++++
+ mech_eap/duplicate_name.c             |   60 ++
+ mech_eap/eap_mech.c                   |  219 ++++++
+ mech_eap/exchange_meta_data.c         |   82 +++
+ mech_eap/export_name.c                |   60 ++
+ mech_eap/export_name_composite.c      |   62 ++
+ mech_eap/export_sec_context.c         |  246 +++++++
+ mech_eap/get_mic.c                    |   89 +++
+ mech_eap/get_name_attribute.c         |   67 ++
+ mech_eap/gssapiP_eap.h                |  410 +++++++++++
+ mech_eap/gssapi_eap.h                 |   90 +++
+ mech_eap/gsseap_err.et                |  162 +++++
+ mech_eap/import_name.c                |   47 ++
+ mech_eap/import_sec_context.c         |  374 ++++++++++
+ mech_eap/indicate_mechs.c             |   44 ++
+ mech_eap/init_sec_context.c           | 1097 ++++++++++++++++++++++++++++++
+ mech_eap/inquire_attrs_for_mech.c     |  137 ++++
+ mech_eap/inquire_context.c            |  116 ++++
+ mech_eap/inquire_cred.c               |   61 ++
+ mech_eap/inquire_cred_by_mech.c       |   76 +++
+ mech_eap/inquire_cred_by_oid.c        |   83 +++
+ mech_eap/inquire_mech_for_saslname.c  |   84 +++
+ mech_eap/inquire_mechs_for_name.c     |   69 ++
+ mech_eap/inquire_name.c               |   75 ++
+ mech_eap/inquire_names_for_mech.c     |   77 +++
+ mech_eap/inquire_saslname_for_mech.c  |   51 ++
+ mech_eap/inquire_sec_context_by_oid.c |  248 +++++++
+ mech_eap/install-sh                   |  520 ++++++++++++++
+ mech_eap/map_name_to_any.c            |   58 ++
+ mech_eap/mech                         |    8 +
+ mech_eap/mech_eap-noacceptor.exports  |   55 ++
+ mech_eap/mech_eap.exports             |   63 ++
+ mech_eap/mech_invoke.c                |   44 ++
+ mech_eap/process_context_token.c      |   71 ++
+ mech_eap/pseudo_random.c              |  195 ++++++
+ mech_eap/query_mechanism_info.c       |   67 ++
+ mech_eap/query_meta_data.c            |  116 ++++
+ mech_eap/radius_ad.exports            |    1 +
+ mech_eap/radsec.conf                  |   12 +
+ mech_eap/radsec_err.et                |   38 +
+ mech_eap/release_any_name_mapping.c   |   59 ++
+ mech_eap/release_cred.c               |   44 ++
+ mech_eap/release_name.c               |   44 ++
+ mech_eap/release_oid.c                |   44 ++
+ mech_eap/set_cred_option.c            |  208 ++++++
+ mech_eap/set_name_attribute.c         |   60 ++
+ mech_eap/set_sec_context_option.c     |   87 +++
+ mech_eap/store_cred.c                 |   83 +++
+ mech_eap/unwrap.c                     |   85 +++
+ mech_eap/unwrap_iov.c                 |  572 ++++++++++++++++
+ mech_eap/util.h                       | 1032 ++++++++++++++++++++++++++++
+ mech_eap/util_adshim.c                |  242 +++++++
+ mech_eap/util_attr.cpp                | 1191 ++++++++++++++++++++++++++++++++
+ mech_eap/util_attr.h                  |  389 +++++++++++
+ mech_eap/util_base64.c                |  161 +++++
+ mech_eap/util_base64.h                |   58 ++
+ mech_eap/util_buffer.c                |  103 +++
+ mech_eap/util_cksum.c                 |  242 +++++++
+ mech_eap/util_context.c               |  377 +++++++++++
+ mech_eap/util_cred.c                  |  756 +++++++++++++++++++++
+ mech_eap/util_crypt.c                 |  397 +++++++++++
+ mech_eap/util_json.cpp                |  513 ++++++++++++++
+ mech_eap/util_json.h                  |  182 +++++
+ mech_eap/util_krb.c                   |  632 +++++++++++++++++
+ mech_eap/util_lucid.c                 |  183 +++++
+ mech_eap/util_mech.c                  |  380 +++++++++++
+ mech_eap/util_moonshot.c              |  238 +++++++
+ mech_eap/util_name.c                  |  789 ++++++++++++++++++++++
+ mech_eap/util_oid.c                   |  206 ++++++
+ mech_eap/util_ordering.c              |  302 +++++++++
+ mech_eap/util_radius.cpp              |  899 +++++++++++++++++++++++++
+ mech_eap/util_radius.h                |  183 +++++
+ mech_eap/util_reauth.c                | 1196 +++++++++++++++++++++++++++++++++
+ mech_eap/util_reauth.h                |  151 +++++
+ mech_eap/util_saml.cpp                |  775 +++++++++++++++++++++
+ mech_eap/util_saml.h                  |  176 +++++
+ mech_eap/util_shib.cpp                |  555 +++++++++++++++
+ mech_eap/util_shib.h                  |  122 ++++
+ mech_eap/util_sm.c                    |  372 ++++++++++
+ mech_eap/util_tld.c                   |  167 +++++
+ mech_eap/util_token.c                 |  493 ++++++++++++++
+ mech_eap/verify_mic.c                 |   71 ++
+ mech_eap/wrap.c                       |  137 ++++
+ mech_eap/wrap_iov.c                   |  379 +++++++++++
+ mech_eap/wrap_iov_length.c            |  234 +++++++
+ mech_eap/wrap_size_limit.c            |   97 +++
+ 117 files changed, 24690 insertions(+), 0 deletions(-)
+ create mode 100644 Makefile.am
+ create mode 100644 acinclude.m4
+ create mode 100755 autogen.sh
+ create mode 100755 build-aux/compile
+ create mode 100644 configure.ac
+ create mode 100644 m4/minuso.m4
+ create mode 100644 mech_eap.spec.in
+ create mode 100644 mech_eap/.gitignore
+ create mode 100644 mech_eap/AUTHORS
+ create mode 100644 mech_eap/COPYING
+ create mode 100644 mech_eap/LICENSE
+ create mode 100644 mech_eap/Makefile.am
+ create mode 100644 mech_eap/NEWS
+ create mode 100644 mech_eap/NOTES
+ create mode 100644 mech_eap/README
+ create mode 100644 mech_eap/README.samba4
+ create mode 100644 mech_eap/TODO
+ create mode 100644 mech_eap/accept_sec_context.c
+ create mode 100644 mech_eap/acquire_cred.c
+ create mode 100644 mech_eap/acquire_cred_with_password.c
+ create mode 100644 mech_eap/add_cred.c
+ create mode 100644 mech_eap/add_cred_with_password.c
+ create mode 100644 mech_eap/authdata_plugin.h
+ create mode 100644 mech_eap/authorize_localname.c
+ create mode 100644 mech_eap/canonicalize_name.c
+ create mode 100644 mech_eap/compare_name.c
+ create mode 100644 mech_eap/context_time.c
+ create mode 100644 mech_eap/delete_name_attribute.c
+ create mode 100644 mech_eap/delete_sec_context.c
+ create mode 100644 mech_eap/dictionary.ukerna
+ create mode 100644 mech_eap/display_name.c
+ create mode 100644 mech_eap/display_name_ext.c
+ create mode 100644 mech_eap/display_status.c
+ create mode 100644 mech_eap/duplicate_name.c
+ create mode 100644 mech_eap/eap_mech.c
+ create mode 100644 mech_eap/exchange_meta_data.c
+ create mode 100644 mech_eap/export_name.c
+ create mode 100644 mech_eap/export_name_composite.c
+ create mode 100644 mech_eap/export_sec_context.c
+ create mode 100644 mech_eap/get_mic.c
+ create mode 100644 mech_eap/get_name_attribute.c
+ create mode 100644 mech_eap/gssapiP_eap.h
+ create mode 100644 mech_eap/gssapi_eap.h
+ create mode 100644 mech_eap/gsseap_err.et
+ create mode 100644 mech_eap/import_name.c
+ create mode 100644 mech_eap/import_sec_context.c
+ create mode 100644 mech_eap/indicate_mechs.c
+ create mode 100644 mech_eap/init_sec_context.c
+ create mode 100644 mech_eap/inquire_attrs_for_mech.c
+ create mode 100644 mech_eap/inquire_context.c
+ create mode 100644 mech_eap/inquire_cred.c
+ create mode 100644 mech_eap/inquire_cred_by_mech.c
+ create mode 100644 mech_eap/inquire_cred_by_oid.c
+ create mode 100644 mech_eap/inquire_mech_for_saslname.c
+ create mode 100644 mech_eap/inquire_mechs_for_name.c
+ create mode 100644 mech_eap/inquire_name.c
+ create mode 100644 mech_eap/inquire_names_for_mech.c
+ create mode 100644 mech_eap/inquire_saslname_for_mech.c
+ create mode 100644 mech_eap/inquire_sec_context_by_oid.c
+ create mode 100755 mech_eap/install-sh
+ create mode 100644 mech_eap/map_name_to_any.c
+ create mode 100644 mech_eap/mech
+ create mode 100644 mech_eap/mech_eap-noacceptor.exports
+ create mode 100644 mech_eap/mech_eap.exports
+ create mode 100644 mech_eap/mech_invoke.c
+ create mode 100644 mech_eap/process_context_token.c
+ create mode 100644 mech_eap/pseudo_random.c
+ create mode 100644 mech_eap/query_mechanism_info.c
+ create mode 100644 mech_eap/query_meta_data.c
+ create mode 100644 mech_eap/radius_ad.exports
+ create mode 100644 mech_eap/radsec.conf
+ create mode 100644 mech_eap/radsec_err.et
+ create mode 100644 mech_eap/release_any_name_mapping.c
+ create mode 100644 mech_eap/release_cred.c
+ create mode 100644 mech_eap/release_name.c
+ create mode 100644 mech_eap/release_oid.c
+ create mode 100644 mech_eap/set_cred_option.c
+ create mode 100644 mech_eap/set_name_attribute.c
+ create mode 100644 mech_eap/set_sec_context_option.c
+ create mode 100644 mech_eap/store_cred.c
+ create mode 100644 mech_eap/unwrap.c
+ create mode 100644 mech_eap/unwrap_iov.c
+ create mode 100644 mech_eap/util.h
+ create mode 100644 mech_eap/util_adshim.c
+ create mode 100644 mech_eap/util_attr.cpp
+ create mode 100644 mech_eap/util_attr.h
+ create mode 100644 mech_eap/util_base64.c
+ create mode 100644 mech_eap/util_base64.h
+ create mode 100644 mech_eap/util_buffer.c
+ create mode 100644 mech_eap/util_cksum.c
+ create mode 100644 mech_eap/util_context.c
+ create mode 100644 mech_eap/util_cred.c
+ create mode 100644 mech_eap/util_crypt.c
+ create mode 100644 mech_eap/util_json.cpp
+ create mode 100644 mech_eap/util_json.h
+ create mode 100644 mech_eap/util_krb.c
+ create mode 100644 mech_eap/util_lucid.c
+ create mode 100644 mech_eap/util_mech.c
+ create mode 100644 mech_eap/util_moonshot.c
+ create mode 100644 mech_eap/util_name.c
+ create mode 100644 mech_eap/util_oid.c
+ create mode 100644 mech_eap/util_ordering.c
+ create mode 100644 mech_eap/util_radius.cpp
+ create mode 100644 mech_eap/util_radius.h
+ create mode 100644 mech_eap/util_reauth.c
+ create mode 100644 mech_eap/util_reauth.h
+ create mode 100644 mech_eap/util_saml.cpp
+ create mode 100644 mech_eap/util_saml.h
+ create mode 100644 mech_eap/util_shib.cpp
+ create mode 100644 mech_eap/util_shib.h
+ create mode 100644 mech_eap/util_sm.c
+ create mode 100644 mech_eap/util_tld.c
+ create mode 100644 mech_eap/util_token.c
+ create mode 100644 mech_eap/verify_mic.c
+ create mode 100644 mech_eap/wrap.c
+ create mode 100644 mech_eap/wrap_iov.c
+ create mode 100644 mech_eap/wrap_iov_length.c
+ create mode 100644 mech_eap/wrap_size_limit.c
+
+diff --git a/Makefile.am b/Makefile.am
+new file mode 100644
+index 0000000..f9690bd
+--- /dev/null
++++ b/Makefile.am
+@@ -0,0 +1,4 @@
++AUTOMAKE_OPTIONS = foreign
++ACLOCAL_AMFLAGS = -I m4
++SUBDIRS = mech_eap
++EXTRA_DIST = mech_eap.spec
+diff --git a/acinclude.m4 b/acinclude.m4
+new file mode 100644
+index 0000000..6f43261
+--- /dev/null
++++ b/acinclude.m4
+@@ -0,0 +1,364 @@
++dnl Based on the one from the Boinc project by Reinhard
++
++AC_DEFUN([AX_CHECK_WINDOWS],
++[AC_MSG_CHECKING(for windows)
++target_windows="no"
++AC_CHECK_HEADER(windows.h,[target_windows="yes"],[target_windows="no"])
++AC_MSG_RESULT($target_windows)
++AM_CONDITIONAL(TARGET_WINDOWS,test "x$target_windows" = "xyes")
++])dnl
++
++AC_DEFUN([AX_CHECK_KRB5],
++[AC_MSG_CHECKING(for GSS-API and Kerberos implementation)
++KRB5_DIR=
++found_krb5="no"
++AC_ARG_WITH(krb5,
++    AC_HELP_STRING([--with-krb5],
++       [Use krb5 (in specified installation directory)]),
++    [check_krb5_dir="$withval"],
++    [check_krb5_dir=])
++for dir in $check_krb5_dir $prefix /usr/local /usr ; do
++   krb5dir="$dir"
++   if test -x "$dir/bin/krb5-config"; then
++     found_krb5="yes";
++     if test "x$target_windows" = "xyes"; then
++        KRB5_CFLAGS=-I"$check_krb5_dir/include";
++        KRB5_LDFLAGS="-L$check_krb5_dir/lib/";
++        KRB5_LIBS="-lkrb5_32 -lgssapi32";
++        COMPILE_ET="$check_krb5_dir/bin/compile_et";
++      AC_MSG_RESULT([yes])
++     else
++        KRB5_CFLAGS=`$dir/bin/krb5-config gssapi --cflags`;
++        KRB5_LDFLAGS="-L$dir/lib";
++        KRB5_LIBS=`$dir/bin/krb5-config gssapi --libs`
++AC_MSG_RESULT([yes])
++        AC_PATH_PROG(COMPILE_ET, [compile_et], [compile_et], [$dir/bin$PATH_SEPARATOr])
++     fi
++     break;
++   fi
++done
++if test x_$found_krb5 != x_yes; then
++   AC_MSG_RESULT($found_krb5)
++   AC_MSG_ERROR([
++----------------------------------------------------------------------
++  Cannot find GSS-API/Kerberos libraries.
++
++  Please install MIT or Heimdal or specify installation directory with
++  --with-krb5=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "Kerberos found in $krb5dir\n";
++      AC_SUBST(KRB5_CFLAGS)
++        AC_SUBST(KRB5_LDFLAGS)
++      AC_SUBST(KRB5_LIBS)
++      AC_SUBST(COMPILE_ET)
++      AC_CHECK_LIB(krb5, GSS_C_NT_COMPOSITE_EXPORT, [AC_DEFINE_UNQUOTED([HAVE_GSS_C_NT_COMPOSITE_EXPORT], 1, [Define if GSS-API library supports recent naming extensions draft])], [], "$KRB5_LIBS")
++      AC_CHECK_LIB(krb5, gss_inquire_attrs_for_mech, [AC_DEFINE_UNQUOTED([HAVE_GSS_INQUIRE_ATTRS_FOR_MECH], 1, [Define if GSS-API library supports RFC 5587])], [], "$KRB5_LIBS")
++      AC_CHECK_LIB(krb5, gss_krb5_import_cred, [AC_DEFINE_UNQUOTED([HAVE_GSS_KRB5_IMPORT_CRED], 1, [Define if GSS-API library supports gss_krb5_import_cred])], [], "$KRB5_LIBS")
++      AC_CHECK_LIB(krb5, heimdal_version, [AC_DEFINE_UNQUOTED([HAVE_HEIMDAL_VERSION], 1, [Define if building against Heimdal Kerberos implementation]), heimdal=yes], [heimdal=no], "$KRB5_LIBS")
++      AM_CONDITIONAL(HEIMDAL, test "x$heimdal" != "xno")
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_EAP],
++[AC_MSG_CHECKING(for EAP implementation)
++EAP_DIR=
++found_eap="no"
++AC_ARG_WITH(eap,
++    AC_HELP_STRING([--with-eap],
++       [Use eap (in specified installation directory)]),
++    [check_eap_dir="$withval"],
++    [check_eap_dir=])
++for dir in $check_eap_dir $prefix /usr /usr/local ../libeap ; do
++   eapdir="$dir"
++   if test -f "$dir/src/eap_peer/eap.h"; then
++     found_eap="yes";
++     EAP_DIR="${eapdir}"
++     EAP_CFLAGS="-I$eapdir/src/common -I$eapdir/src -I$eapdir/src/utils";
++     break;
++   fi
++done
++AC_MSG_RESULT($found_eap)
++if test x_$found_eap != x_yes; then
++   AC_MSG_ERROR([
++----------------------------------------------------------------------
++  Cannot find EAP libraries.
++
++  Please install wpa_supplicant or specify installation directory with
++  --with-eap=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "EAP found in $eapdir\n";
++      EAP_CFLAGS="$EAP_CFLAGS \
++-DEAP_TLS \
++-DEAP_PEAP \
++-DEAP_TTLS \
++-DEAP_MD5 \
++-DEAP_MSCHAPv2 \
++-DEAP_GTC \
++-DEAP_OTP \
++-DEAP_LEAP \
++-DEAP_PSK \
++-DEAP_PAX \
++-DEAP_SAKE \
++-DEAP_GPSK \
++-DEAP_GPSK_SHA256 \
++-DEAP_SERVER_IDENTITY \
++-DEAP_SERVER_TLS \
++-DEAP_SERVER_PEAP \
++-DEAP_SERVER_TTLS \
++-DEAP_SERVER_MD5 \
++-DEAP_SERVER_MSCHAPV2 \
++-DEAP_SERVER_GTC \
++-DEAP_SERVER_PSK \
++-DEAP_SERVER_PAX \
++-DEAP_SERVER_SAKE \
++-DEAP_SERVER_GPSK \
++-DEAP_SERVER_GPSK_SHA256 \
++-DIEEE8021X_EAPOL";
++      EAP_LIBS="-leap -lutils -lcrypto -ltls";
++      EAP_LDFLAGS="-L$eapdir/eap_example -L$eapdir/src/utils -L$eapdir/src/crypto -L$eapdir/src/tls";
++      AC_SUBST(EAP_CFLAGS)
++      AC_SUBST(EAP_LDFLAGS)
++      AC_SUBST(EAP_LIBS)
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_SHIBSP],
++[AC_MSG_CHECKING(for Shibboleth implementation)
++SHIBSP_DIR=
++found_shibsp="no"
++AC_ARG_WITH(shibsp,
++    AC_HELP_STRING([--with-shibsp],
++       [Use shibspboleth (in specified installation directory)]),
++    [check_shibsp_dir="$withval"],
++    [check_shibsp_dir=])
++for dir in $check_shibsp_dir $prefix /usr /usr/local ; do
++   shibspdir="$dir"
++   if test -f "$dir/include/shibsp/SPConfig.h"; then
++     found_shibsp="yes";
++     SHIBSP_DIR="${shibspdir}"
++     SHIBSP_CXXFLAGS="-I$shibspdir/include";
++     break;
++   fi
++done
++AC_MSG_RESULT($found_shibsp)
++if test x_$found_shibsp != x_yes; then
++   AC_MSG_ERROR([
++----------------------------------------------------------------------
++  Cannot find Shibboleth libraries.
++
++  Please install Shibboleth or specify installation directory with
++  --with-shibsp=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "Shibboleth found in $shibspdir\n";
++      SHIBSP_LIBS="-lshibsp -lsaml -lxml-security-c -lxmltooling -lxerces-c";
++      SHIBSP_LDFLAGS="-L$shibspdir/lib";
++      AC_SUBST(SHIBSP_CXXFLAGS)
++      AC_SUBST(SHIBSP_LDFLAGS)
++      AC_SUBST(SHIBSP_LIBS)
++      AC_DEFINE_UNQUOTED([HAVE_SHIBSP], 1, [Define is Shibboleth SP is available])
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_SHIBRESOLVER],
++[AC_MSG_CHECKING(for Shibboleth resolver implementation)
++SHIBRESOLVER_DIR=
++found_shibresolver="no"
++AC_ARG_WITH(shibresolver,
++    AC_HELP_STRING([--with-shibresolver],
++       [Use Shibboleth resolver (in specified installation directory)]),
++    [check_shibresolver_dir="$withval"],
++    [check_shibresolver_dir=])
++if test x_$check_shibresolver_dir != x_no; then
++for dir in $check_shibresolver_dir $prefix /usr /usr/local ; do
++   shibresolverdir="$dir"
++   if test -f "$dir/include/shibresolver/resolver.h"; then
++     found_shibresolver="yes";
++     SHIBRESOLVER_DIR="${shibresolverdir}"
++     SHIBRESOLVER_CXXFLAGS="-I$shibresolverdir/include";
++     break;
++   fi
++done
++fi
++AC_MSG_RESULT($found_shibresolver)
++if test x_$check_shibresolver_dir != x_no; then
++if test x_$found_shibresolver != x_yes; then
++   AC_MSG_WARN([
++----------------------------------------------------------------------
++  Cannot find Shibboleth resolver libraries, building without
++  Shibboleth support.
++
++  Please install Shibboleth or specify installation directory with
++  --with-shibresolver=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "Shibboleth resolver found in $shibresolverdir\n";
++      SHIBRESOLVER_LIBS="-lshibresolver";
++      SHIBRESOLVER_LDFLAGS="-L$shibresolverdir/lib";
++      AC_SUBST(SHIBRESOLVER_CXXFLAGS)
++      AC_SUBST(SHIBRESOLVER_LDFLAGS)
++      AC_SUBST(SHIBRESOLVER_LIBS)
++      AC_DEFINE_UNQUOTED([HAVE_SHIBRESOLVER], 1, [Define is Shibboleth resolver is available])
++fi
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_OPENSAML],
++[AC_MSG_CHECKING(for OpenSAML implementation)
++OPENSAML_DIR=
++found_opensaml="no"
++AC_ARG_WITH(opensaml,
++    AC_HELP_STRING([--with-opensaml],
++       [Use OpenSAML (in specified installation directory)]),
++    [check_opensaml_dir="$withval"],
++    [check_opensaml_dir=])
++if test x_$check_opensaml_dir != x_no; then
++for dir in $check_opensaml_dir $prefix /usr /usr/local ; do
++   opensamldir="$dir"
++   if test -f "$dir/include/saml/Assertion.h"; then
++     found_opensaml="yes";
++     OPENSAML_DIR="${opensamldir}"
++     OPENSAML_CXXFLAGS="-I$opensamldir/include";
++     break;
++   fi
++done
++fi
++AC_MSG_RESULT($found_opensaml)
++if test x_$check_opensaml_dir != x_no; then
++if test x_$found_opensaml != x_yes; then
++   AC_MSG_WARN([
++----------------------------------------------------------------------
++  Cannot find OpenSAML libraries, building without OpenSAML support.
++
++  Please install OpenSAML or specify installation directory with
++  --with-opensaml=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "OpenSAML found in $opensamldir\n";
++      OPENSAML_LIBS="-lsaml -lxml-security-c -lxmltooling -lxerces-c";
++      OPENSAML_LDFLAGS="-L$opensamldir/lib";
++      AC_SUBST(OPENSAML_CXXFLAGS)
++      AC_SUBST(OPENSAML_LDFLAGS)
++      AC_SUBST(OPENSAML_LIBS)
++      AC_DEFINE_UNQUOTED([HAVE_OPENSAML], 1, [Define is OpenSAML is available])
++fi
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_RADSEC],
++[AC_MSG_CHECKING(for radsec)
++RADSEC_DIR=
++found_radsec="no"
++AC_ARG_WITH(radsec,
++    AC_HELP_STRING([--with-radsec],
++       [Use radsec (in specified installation directory)]),
++    [check_radsec_dir="$withval"],
++    [check_radsec_dir=])
++for dir in $check_radsec_dir $prefix /usr /usr/local ; do
++   radsecdir="$dir"
++   if test -f "$dir/include/radsec/radsec.h"; then
++     found_radsec="yes";
++     RADSEC_DIR="${radsecdir}"
++     RADSEC_CFLAGS="-I$radsecdir/include";
++     break;
++   fi
++done
++AC_MSG_RESULT($found_radsec)
++if test x_$found_radsec != x_yes; then
++   AC_MSG_ERROR([
++----------------------------------------------------------------------
++  Cannot find radsec libraries.
++
++  Please install libradsec or specify installation directory with
++  --with-radsec=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "radsec found in $radsecdir\n";
++      RADSEC_LIBS="-lradsec";
++      RADSEC_LDFLAGS="-L$radsecdir/lib";
++      AC_SUBST(RADSEC_CFLAGS)
++      AC_SUBST(RADSEC_LDFLAGS)
++      AC_SUBST(RADSEC_LIBS)
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_JANSSON],
++[AC_MSG_CHECKING(for jansson)
++JANSSON_DIR=
++found_jansson="no"
++AC_ARG_WITH(jansson,
++    AC_HELP_STRING([--with-jansson],
++       [Use jansson (in specified installation directory)]),
++    [check_jansson_dir="$withval"],
++    [check_jansson_dir=])
++for dir in $check_jansson_dir $prefix /usr /usr/local ; do
++   janssondir="$dir"
++   if test -f "$dir/include/jansson.h"; then
++     found_jansson="yes";
++     JANSSON_DIR="${janssondir}"
++     JANSSON_CFLAGS="-I$janssondir/include";
++     break;
++   fi
++done
++AC_MSG_RESULT($found_jansson)
++if test x_$found_jansson != x_yes; then
++   AC_MSG_ERROR([
++----------------------------------------------------------------------
++  Cannot find jansson libraries.
++
++  Please install libjansson or specify installation directory with
++  --with-jansson=(dir).
++----------------------------------------------------------------------
++])
++else
++      printf "jansson found in $janssondir\n";
++      JANSSON_LIBS="-ljansson";
++      JANSSON_LDFLAGS="-L$janssondir/lib";
++      AC_SUBST(JANSSON_CFLAGS)
++      AC_SUBST(JANSSON_LDFLAGS)
++      AC_SUBST(JANSSON_LIBS)
++fi
++])dnl
++
++AC_DEFUN([AX_CHECK_LIBMOONSHOT],
++[AC_MSG_CHECKING(for Moonshot identity selector implementation)
++LIBMOONSHOT_DIR=
++LIBMOONSHOT_CFLAGS=
++LIBMOONSHOT_LDFLAGS=
++LIBMOONSHOT_LIBS=
++found_libmoonshot="no"
++AC_ARG_WITH(libmoonshot,
++    AC_HELP_STRING([--with-libmoonshot],
++       [Use libmoonshot (in specified installation directory)]),
++    [check_libmoonshot_dir="$withval"],
++    [check_libmoonshot_dir=])
++for dir in $check_libmoonshot_dir $prefix /usr /usr/local ; do
++   libmoonshotdir="$dir"
++   if test -f "$dir/include/libmoonshot.h"; then
++     found_libmoonshot="yes";
++     LIBMOONSHOT_DIR="${libmoonshotdir}"
++     LIBMOONSHOT_CFLAGS="-I$libmoonshotdir/include";
++     break;
++   fi
++done
++AC_MSG_RESULT($found_libmoonshot)
++if test x_$found_libmoonshot = x_yes; then
++    printf "libmoonshot found in $libmoonshotdir\n";
++    LIBMOONSHOT_LIBS="-lmoonshot";
++    LIBMOONSHOT_LDFLAGS="-L$libmoonshot/lib";
++    AC_CHECK_LIB(moonshot, moonshot_get_identity, [AC_DEFINE_UNQUOTED([HAVE_MOONSHOT_GET_IDENTITY], 1, [Define if Moonshot identity selector is available])], [], "$LIBMOONSHOT_LIBS")
++fi
++    AC_SUBST(LIBMOONSHOT_CFLAGS)
++    AC_SUBST(LIBMOONSHOT_LDFLAGS)
++    AC_SUBST(LIBMOONSHOT_LIBS)
++    AM_CONDITIONAL(LIBMOONSHOT, test "x$found_libmoonshot" != "xno")
++])dnl
++
+diff --git a/autogen.sh b/autogen.sh
+new file mode 100755
+index 0000000..13432d0
+--- /dev/null
++++ b/autogen.sh
+@@ -0,0 +1,16 @@
++#!/bin/sh
++#
++# Regenerate autotools files.
++#
++
++PATH=/usr/local/bin:$PATH
++
++if [ -x "`which autoreconf 2>/dev/null`" ] ; then
++   exec autoreconf -ivf
++fi
++
++aclocal -I . -I m4 && \
++    autoheader && \
++    libtoolize --automake -c && \
++    autoconf && \
++    automake --add-missing --copy
+diff --git a/build-aux/compile b/build-aux/compile
+new file mode 100755
+index 0000000..5360806
+--- /dev/null
++++ b/build-aux/compile
+@@ -0,0 +1,144 @@
++#! /bin/sh
++# Wrapper for compilers which do not understand `-c -o'.
++
++scriptversion=2009-10-06.20; # UTC
++
++# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009  Free Software
++# Foundation, Inc.
++# Written by Tom Tromey <tromey@cygnus.com>.
++#
++# 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, or (at your option)
++# any later version.
++#
++# 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.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
++
++# As a special exception to the GNU General Public License, if you
++# distribute this file as part of a program that contains a
++# configuration script generated by Autoconf, you may include it under
++# the same distribution terms that you use for the rest of that program.
++
++# This file is maintained in Automake, please report
++# bugs to <bug-automake@gnu.org> or send patches to
++# <automake-patches@gnu.org>.
++
++case $1 in
++  '')
++     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
++     exit 1;
++     ;;
++  -h | --h*)
++    cat <<\EOF
++Usage: compile [--help] [--version] PROGRAM [ARGS]
++
++Wrapper for compilers which do not understand `-c -o'.
++Remove `-o dest.o' from ARGS, run PROGRAM with the remaining
++arguments, and rename the output as expected.
++
++If you are trying to build a whole package this is not the
++right script to run: please start by reading the file `INSTALL'.
++
++Report bugs to <bug-automake@gnu.org>.
++EOF
++    exit $?
++    ;;
++  -v | --v*)
++    echo "compile $scriptversion"
++    exit $?
++    ;;
++esac
++
++ofile=
++cfile=
++eat=
++
++for arg
++do
++  if test -n "$eat"; then
++    eat=
++  else
++    case $1 in
++      -o)
++      # configure might choose to run compile as `compile cc -o foo foo.c'.
++      # So we strip `-o arg' only if arg is an object.
++      eat=1
++      case $2 in
++        *.o | *.obj)
++          ofile=$2
++          ;;
++        *)
++          set x "$@" -o "$2"
++          shift
++          ;;
++      esac
++      ;;
++      *.c)
++      cfile=$1
++      set x "$@" "$1"
++      shift
++      ;;
++      *)
++      set x "$@" "$1"
++      shift
++      ;;
++    esac
++  fi
++  shift
++done
++
++if test -z "$ofile" || test -z "$cfile"; then
++  # If no `-o' option was seen then we might have been invoked from a
++  # pattern rule where we don't need one.  That is ok -- this is a
++  # normal compilation that the losing compiler can handle.  If no
++  # `.c' file was seen then we are probably linking.  That is also
++  # ok.
++  exec "$@"
++fi
++
++# Name of file we expect compiler to create.
++cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
++
++# Create the lock directory.
++# Note: use `[/\\:.-]' here to ensure that we don't use the same name
++# that we are using for the .o file.  Also, base the name on the expected
++# object file name, since that is what matters with a parallel build.
++lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
++while true; do
++  if mkdir "$lockdir" >/dev/null 2>&1; then
++    break
++  fi
++  sleep 1
++done
++# FIXME: race condition here if user kills between mkdir and trap.
++trap "rmdir '$lockdir'; exit 1" 1 2 15
++
++# Run the compile.
++"$@"
++ret=$?
++
++if test -f "$cofile"; then
++  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
++elif test -f "${cofile}bj"; then
++  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
++fi
++
++rmdir "$lockdir"
++exit $ret
++
++# Local Variables:
++# mode: shell-script
++# sh-indentation: 2
++# eval: (add-hook 'write-file-hooks 'time-stamp)
++# time-stamp-start: "scriptversion="
++# time-stamp-format: "%:y-%02m-%02d.%02H"
++# time-stamp-time-zone: "UTC"
++# time-stamp-end: "; # UTC"
++# End:
+diff --git a/configure.ac b/configure.ac
+new file mode 100644
+index 0000000..ba23c32
+--- /dev/null
++++ b/configure.ac
+@@ -0,0 +1,92 @@
++AC_PREREQ([2.61])
++AC_INIT([mech_eap], [0.1], [bugs@project-moonshot.org])
++AC_CONFIG_MACRO_DIR([m4])
++AC_CONFIG_AUX_DIR([build-aux])
++
++dnl AM_INIT_AUTOMAKE([silent-rules])
++AC_USE_SYSTEM_EXTENSIONS
++AM_INIT_AUTOMAKE
++AM_PROG_CC_C_O
++AM_MAINTAINER_MODE()
++LT_PREREQ([2.2])
++LT_INIT([dlopen disable-static win32-dll])
++
++dnl AC_PROG_CC
++AC_PROG_CXX
++AC_CONFIG_HEADERS([config.h])
++AC_CHECK_HEADERS(stdarg.h stdio.h stdint.h sys/param.h)
++AC_REPLACE_FUNCS(vasprintf)
++
++dnl Check if we're on Solaris and set CFLAGS accordingly
++dnl AC_CANONICAL_TARGET
++dnl case "${target_os}" in
++dnl   solaris*)
++dnl     TARGET_CFLAGS="-DSYS_SOLARIS9 -D_POSIX_PTHREAD_SEMANTICS"
++dnl     if test "$GCC" != yes ; then
++dnl       TARGET_CFLAGS="$TARGET_CFLAGS -mt"
++dnl     else
++dnl       TARGET_CFLAGS="$TARGET_CFLAGS -pthreads"
++dnl     fi
++dnl     TARGET_LDFLAGS="-lpthread -lsocket -lnsl"
++dnl     ;;
++dnl   *)
++dnl     TARGET_CFLAGS="-Wall -pedantic -pthread"
++dnl     TARGET_LDFLAGS=""
++dnl   esac
++
++reauth=no
++AC_ARG_ENABLE(reauth,
++  [  --enable-reauth whether to enable fast reauthentication protocol: yes/no; default no ],
++  [ if test "x$enableval" = "xyes" -o "x$enableval" = "xno" ; then
++      reauth=$enableval
++    else
++      echo "--enable-reauth argument must be yes or no"
++      exit -1
++    fi
++  ])
++
++if test "x$reauth" = "xyes" ; then
++  echo "Fast reauthentication protocol enabled"
++  TARGET_CFLAGS="$TARGET_CFLAGS -DGSSEAP_ENABLE_REAUTH"
++fi
++AM_CONDITIONAL(GSSEAP_ENABLE_REAUTH, test "x$reauth" != "xno")
++
++acceptor=yes
++AC_ARG_ENABLE(acceptor,
++  [  --enable-acceptor whether to enable acceptor codepaths: yes/no; default yes ],
++  [ if test "x$enableval" = "xyes" -o "x$enableval" = "xno" ; then
++      acceptor=$enableval
++    else
++      echo "--enable-acceptor argument must be yes or no"
++      exit -1
++    fi
++  ])
++
++if test "x$acceptor" = "xyes" ; then
++  echo "acceptor enabled"
++  TARGET_CFLAGS="$TARGET_CFLAGS -DGSSEAP_ENABLE_ACCEPTOR"
++fi
++AM_CONDITIONAL(GSSEAP_ENABLE_ACCEPTOR, test "x$acceptor" != "xno")
++
++AC_SUBST(TARGET_CFLAGS)
++AC_SUBST(TARGET_LDFLAGS)
++AX_CHECK_WINDOWS
++AX_CHECK_KRB5
++AX_CHECK_OPENSAML
++AM_CONDITIONAL(OPENSAML, test "x_$check_opensaml_dir" != "x_no")
++
++AX_CHECK_SHIBRESOLVER
++AM_CONDITIONAL(SHIBRESOLVER, test "x_$check_shibresolver_dir" != "x_no")
++if test x_$found_shibresolver = x_yes; then
++  AX_CHECK_SHIBSP
++fi
++
++if test "x$acceptor" = "xyes" ; then
++  AX_CHECK_RADSEC
++  AX_CHECK_JANSSON
++fi
++
++AX_CHECK_LIBMOONSHOT
++AC_CONFIG_FILES([Makefile mech_eap/Makefile
++                        mech_eap.spec])
++AC_OUTPUT
+diff --git a/m4/minuso.m4 b/m4/minuso.m4
+new file mode 100644
+index 0000000..d8b1620
+--- /dev/null
++++ b/m4/minuso.m4
+@@ -0,0 +1,35 @@
++##                                                          -*- Autoconf -*-
++# Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008
++# Free Software Foundation, Inc.
++#
++# This file 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.
++
++# serial 6
++
++# AM_PROG_CC_C_O
++# --------------
++# Like AC_PROG_CC_C_O, but changed for automake.
++AC_DEFUN([AM_PROG_CC_C_O],
++[AC_REQUIRE([AC_PROG_CC_C_O])dnl
++AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
++AC_REQUIRE_AUX_FILE([compile])dnl
++# FIXME: we rely on the cache variable name because
++# there is no other way.
++set dummy $CC
++am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
++eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
++if test "$am_t" != yes; then
++   # Losing compiler, so override with the script.
++   # FIXME: It is wrong to rewrite CC.
++   # But if we don't then we get into trouble of one sort or another.
++   # A longer-term fix would be to have automake use am__CC in this case,
++   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
++   CC="$am_aux_dir/compile $CC"
++fi
++dnl Make sure AC_PROG_CC is never called again, or it will override our
++dnl setting of CC.
++m4_define([AC_PROG_CC],
++          [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])])
++])
+diff --git a/mech_eap.spec.in b/mech_eap.spec.in
+new file mode 100644
+index 0000000..90ac6cf
+--- /dev/null
++++ b/mech_eap.spec.in
+@@ -0,0 +1,62 @@
++%global _moonshot_krb5 %{!?_moonshot_krb5:krb5-devel}%{?_moonshot_krb5}
++Name:         moonshot-gss-eap
++Version:      @VERSION@
++Release:      3%{?dist}
++Summary:      Moonshot GSS-API Mechanism
++
++Group:                Security Tools
++License:      BSD
++URL:          http://www.project-moonshot.org/
++Source0:      mech_eap-%{version}.tar.gz
++BuildRoot:    %{_tmppath}/%{name}-%{version}-%{release}-root
++
++BuildRequires:         %{_moonshot_krb5} >= 1.9.1
++BuildRequires:         moonshot-ui-devel
++BuildRequires: jansson-devel
++Requires:     moonshot-ui
++BuildRequires: libradsec-devel
++BuildRequires: shibboleth-devel >= 2.5
++BuildRequires: libshibresolver-devel
++
++
++
++%description
++Project Moonshot provides federated access management.
++
++
++%prep
++%setup -q -n mech_eap-%{version}
++
++
++%build
++      export LDFLAGS='-L/usr/%{_lib}/freeradius -Wl,--rpath=/usr/%{_lib}/freeradius'
++%configure  --with-libmoonshot=%{_prefix} --with-krb5=%{_prefix} --disable-reauth
++make %{?_smp_mflags}
++
++
++%install
++rm -rf $RPM_BUILD_ROOT
++make install DESTDIR=$RPM_BUILD_ROOT
++
++
++%clean
++rm -rf $RPM_BUILD_ROOT
++
++
++%files
++%defattr(-,root,root,-)
++%doc mech_eap/README
++%doc mech_eap/LICENSE
++%doc mech_eap/AUTHORS
++%{_libdir}/gss/mech_eap.so
++%exclude %{_libdir}/gss/mech_eap.la
++%{_includedir}/gssapi/*.h
++#%exclude %{_libdir}/krb5/plugins/authdata/*la
++#%{_libdir}/krb5/plugins/authdata/*.so
++
++
++
++%changelog
++* Wed Sep 28 2011  <hartmans@moonbuildcentos.dev.ja.net> - @VERSION@-2
++- Add radius_ad plugin
++
+diff --git a/mech_eap/.gitignore b/mech_eap/.gitignore
+new file mode 100644
+index 0000000..06a3924
+--- /dev/null
++++ b/mech_eap/.gitignore
+@@ -0,0 +1,32 @@
++/aclocal.m4
++/autom4te.cache
++/compile
++/config.guess
++/config.log
++/config.status
++/config.sub
++/config.h
++/configure
++/config.h.in
++/depcomp
++
++/libtool
++/ltmain.sh
++/missing
++
++/gsseap_err.[ch]
++/radsec_err.[ch]
++
++.DS_Store
++
++Makefile.in
++Makefile
++
++*.la
++*.lo
++*~
++
++.deps
++.libs
++.a.out.dSYM
++.dSYM
+diff --git a/mech_eap/AUTHORS b/mech_eap/AUTHORS
+new file mode 100644
+index 0000000..3007a4b
+--- /dev/null
++++ b/mech_eap/AUTHORS
+@@ -0,0 +1,6 @@
++The initial implementation of mech_eap was written by PADL Software
++under contract to JANET(UK).
++
++--
++Luke Howard <lukeh@padl.com>
++January, 2011
+diff --git a/mech_eap/COPYING b/mech_eap/COPYING
+new file mode 100644
+index 0000000..7554e77
+--- /dev/null
++++ b/mech_eap/COPYING
+@@ -0,0 +1,3 @@
++Copyright (c) 2011, JANET(UK)
++
++See the LICENSE file for licensing terms.
+diff --git a/mech_eap/LICENSE b/mech_eap/LICENSE
+new file mode 100644
+index 0000000..1b03a95
+--- /dev/null
++++ b/mech_eap/LICENSE
+@@ -0,0 +1,31 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
+diff --git a/mech_eap/Makefile.am b/mech_eap/Makefile.am
+new file mode 100644
+index 0000000..23de6af
+--- /dev/null
++++ b/mech_eap/Makefile.am
+@@ -0,0 +1,189 @@
++AUTOMAKE_OPTIONS = foreign
++
++EXTRA_DIST = gsseap_err.et radsec_err.et \
++      mech_eap.exports mech_eap-noacceptor.exports  radius_ad.exports \
++      LICENSE AUTHORS
++
++
++gssincludedir = $(includedir)/gssapi
++gssinclude_HEADERS = gssapi_eap.h
++
++EAP_CFLAGS = -I$(srcdir)/../libeap/src -I$(srcdir)/../libeap/src/common -I$(srcdir)/../libeap/src/eap_common  \
++      -I$(srcdir)/../libeap/src/utils
++
++if GSSEAP_ENABLE_ACCEPTOR
++GSSEAP_EXPORTS = mech_eap.exports
++else
++GSSEAP_EXPORTS = mech_eap-noacceptor.exports
++endif
++
++gssdir = $(libdir)/gss
++gss_LTLIBRARIES = mech_eap.la
++
++if TARGET_WINDOWS
++EAP_CFLAGS += -DCONFIG_WIN32_DEFAULTS -DUSE_INTERNAL_CRYPTO
++OS_LIBS = -lshell32 -ladvapi32 -lws2_32 -lcomerr32
++mech_eap_la_CFLAGS   = -Zi
++mech_eap_la_CXXFLAGS = -Zi
++else
++EAP_CFLAGS += -DEAP_TLS -DEAP_PEAP -DEAP_TTLS -DEAP_MD5 -DEAP_MSCHAPv2 -DEAP_GTC -DEAP_OTP -DEAP_LEAP -DEAP_PSK -DEAP_PAX -DEAP_SAKE -DEAP_GPSK -DEAP_GPSK_SHA256 -DEAP_SERVER_IDENTITY -DEAP_SERVER_TLS -DEAP_SERVER_PEAP -DEAP_SERVER_TTLS -DEAP_SERVER_MD5 -DEAP_SERVER_MSCHAPV2 -DEAP_SERVER_GTC -DEAP_SERVER_PSK -DEAP_SERVER_PAX -DEAP_SERVER_SAKE -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256 -DIEEE8021X_EAPOL
++OS_LIBS =
++mech_eap_la_CFLAGS   = -Werror -Wall -Wunused-parameter
++mech_eap_la_CXXFLAGS = -Werror -Wall -Wunused-parameter
++endif
++mech_eap_la_DEPENDENCIES = $(GSSEAP_EXPORTS)
++
++mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" -DDATAROOTDIR=\"${datarootdir}\"
++mech_eap_la_CFLAGS   += \
++                      @KRB5_CFLAGS@ @RADSEC_CFLAGS@ @TARGET_CFLAGS@ $(EAP_CFLAGS)
++mech_eap_la_CXXFLAGS += \
++                      @KRB5_CFLAGS@ @RADSEC_CFLAGS@ \
++                      @OPENSAML_CXXFLAGS@ @SHIBRESOLVER_CXXFLAGS@ @SHIBSP_CXXFLAGS@ \
++                      @TARGET_CFLAGS@ $(EAP_CFLAGS)
++mech_eap_la_LDFLAGS  = -avoid-version -module \
++                      -export-symbols $(GSSEAP_EXPORTS) -no-undefined \
++                      @KRB5_LDFLAGS@ @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@
++if TARGET_WINDOWS
++mech_eap_la_LDFLAGS += -debug
++endif
++
++mech_eap_la_LIBADD   = @KRB5_LIBS@ ../libeap/libeap.la @RADSEC_LIBS@ \
++                     @OPENSAML_LIBS@ @SHIBRESOLVER_LIBS@ @SHIBSP_LIBS@ @JANSSON_LIBS@
++mech_eap_la_SOURCES =                         \
++      acquire_cred.c                          \
++      acquire_cred_with_password.c            \
++      add_cred.c                              \
++      add_cred_with_password.c                \
++      authorize_localname.c                   \
++      canonicalize_name.c                     \
++      compare_name.c                          \
++      context_time.c                          \
++      delete_sec_context.c                    \
++      display_name.c                          \
++      display_name_ext.c                      \
++      display_status.c                        \
++      duplicate_name.c                        \
++      eap_mech.c                              \
++      exchange_meta_data.c                    \
++      export_name.c                           \
++      export_sec_context.c                    \
++      get_mic.c                               \
++      gsseap_err.c                            \
++      import_name.c                           \
++      import_sec_context.c                    \
++      indicate_mechs.c                        \
++      init_sec_context.c                      \
++      inquire_attrs_for_mech.c                \
++      inquire_context.c                       \
++      inquire_cred.c                          \
++      inquire_cred_by_mech.c                  \
++      inquire_cred_by_oid.c                   \
++      inquire_mech_for_saslname.c             \
++      inquire_mechs_for_name.c                \
++      inquire_names_for_mech.c                \
++      inquire_saslname_for_mech.c             \
++      inquire_sec_context_by_oid.c            \
++      process_context_token.c                 \
++      pseudo_random.c                         \
++      query_mechanism_info.c                  \
++      query_meta_data.c                       \
++      radsec_err.c                            \
++      release_cred.c                          \
++      release_name.c                          \
++      release_oid.c                           \
++      set_cred_option.c                       \
++      set_sec_context_option.c                \
++      store_cred.c                            \
++      unwrap.c                                \
++      unwrap_iov.c                            \
++      util_buffer.c                           \
++      util_context.c                          \
++      util_cksum.c                            \
++      util_cred.c                             \
++      util_crypt.c                            \
++      util_krb.c                              \
++      util_lucid.c                            \
++      util_mech.c                             \
++      util_name.c                             \
++      util_oid.c                              \
++      util_ordering.c                         \
++      util_sm.c                               \
++      util_tld.c                              \
++      util_token.c                            \
++      verify_mic.c                            \
++      wrap.c                                  \
++      wrap_iov.c                              \
++      wrap_iov_length.c                       \
++      wrap_size_limit.c \
++      gssapiP_eap.h \
++      util_attr.h \
++      util_base64.h \
++      util.h \
++      util_json.h \
++      util_radius.h \
++      util_reauth.h \
++      util_saml.h \
++      util_shib.h
++
++if LIBMOONSHOT
++mech_eap_la_SOURCES += util_moonshot.c
++mech_eap_la_CFLAGS  += @LIBMOONSHOT_CFLAGS@
++mech_eap_la_LDFLAGS += @LIBMOONSHOT_LDFLAGS@
++mech_eap_la_LIBADD  += @LIBMOONSHOT_LIBS@
++endif
++
++
++if GSSEAP_ENABLE_ACCEPTOR
++
++mech_eap_la_SOURCES +=                                \
++      accept_sec_context.c                    \
++      delete_name_attribute.c                 \
++      export_name_composite.c                 \
++      get_name_attribute.c                    \
++      inquire_name.c                          \
++      map_name_to_any.c                       \
++      release_any_name_mapping.c              \
++      set_name_attribute.c                    \
++      util_attr.cpp                           \
++      util_base64.c                           \
++      util_json.cpp                           \
++      util_radius.cpp
++
++if OPENSAML
++mech_eap_la_SOURCES += util_saml.cpp
++endif
++
++if SHIBRESOLVER
++mech_eap_la_SOURCES += util_shib.cpp
++endif
++
++endif
++
++BUILT_SOURCES = gsseap_err.c radsec_err.c gsseap_err.h radsec_err.h
++
++if GSSEAP_ENABLE_REAUTH
++mech_eap_la_SOURCES += util_reauth.c
++
++if !HEIMDAL
++krb5pluginsdir = $(libdir)/krb5/plugins/authdata
++krb5plugins_LTLIBRARIES = radius_ad.la
++
++radius_ad_la_CFLAGS  = -Werror -Wall -Wunused-parameter \
++                      @KRB5_CFLAGS@ $(EAP_CFLAGS) @RADSEC_CFLAGS@ @TARGET_CFLAGS@
++radius_ad_la_LDFLAGS = -avoid-version -module \
++                     -export-symbols radius_ad.exports -no-undefined
++radius_ad_la_LIBADD  = @KRB5_LIBS@
++radius_ad_la_SOURCES = util_adshim.c authdata_plugin.h
++endif
++endif
++
++gsseap_err.h gsseap_err.c: gsseap_err.et
++      $(COMPILE_ET) $<
++
++radsec_err.h radsec_err.c: radsec_err.et
++      $(COMPILE_ET) $<
++
++radsec_err.c: radsec_err.h
++
++clean-generic:
++      rm -f gsseap_err.[ch] radsec_err.[ch]
+diff --git a/mech_eap/NEWS b/mech_eap/NEWS
+new file mode 100644
+index 0000000..e69de29
+diff --git a/mech_eap/NOTES b/mech_eap/NOTES
+new file mode 100644
+index 0000000..849ce4e
+--- /dev/null
++++ b/mech_eap/NOTES
+@@ -0,0 +1,9 @@
++- gss_xxx routines acquire lock, gssXxx don't
++
++- git
++
++If you do want to update with a rebase, deletethe branch from the
++server first then push the rebased branch
++
++to delete a branch from a server git push origin :branch_to_del
++
+diff --git a/mech_eap/README b/mech_eap/README
+new file mode 100644
+index 0000000..3cb2d50
+--- /dev/null
++++ b/mech_eap/README
+@@ -0,0 +1,147 @@
++Overview
++========
++
++This is an implementation of the GSS EAP mechanism, as described in
++draft-ietf-abfab-gss-eap-01.txt.
++
++Building
++========
++
++In order to build this, a recent Kerberos implementation (MIT or
++Heimdal), Shibboleth, and EAP libraries are required, along with
++all of their dependencies.
++
++Note: not all SPIs are supported by the Heimdal mechanism glue,
++so not all features will be available.
++
++Installing
++==========
++
++GSS mechglue
++------------
++
++When installing, be sure to edit $prefix/etc/gss/mech to register
++the EAP mechanisms. A sample configuration file is in this directory.
++You may need to specify an absolute path.
++
++RADIUS client library
++---------------------
++
++Make sure your RADIUS library is configured to talk to the server of
++your choice: see the example radsec.conf in this directory. If you
++want to use TCP or TLS, you'll need to run radsecproxy in front of
++your RADIUS server.
++
++RADIUS server
++-------------
++
++These instructions apply to FreeRADIUS only, which is downloadable
++from http://freeradius.org/. After configure, make, install, do the
++following:
++
++On the RADIUS server side, you need to install dictionary.ukerna to
++$prefix/etc/raddb and include it from the main dictionary file, by
++adding:
++
++    $INCLUDE dictionary.ukerna
++
++to $prefix/etc/raddb/dictionary. Make sure these files are world-
++readable; they weren't in my installation.
++
++Edit $prefix/etc/raddb/users to add your test user and password:
++
++    bob@PROJECT-MOONSHOT.ORG Cleartext-Password := secret
++
++Add an entry for your acceptor to $prefix/etc/raddb/clients.conf:
++
++    client somehost {
++        ipaddr = 127.0.0.1
++        secret = testing123
++        require_message_authenticator = yes
++    }
++
++Edit $prefix/etc/raddb/eap.conf and set:
++
++    eap {
++...
++        default_eap_type = ttls
++...
++        tls {
++            certdir = ...
++            cadir = ...
++            private_key_file = ...
++            certificate_file = ...
++        }
++        ttls {
++            default_eap_type = mschapv2
++            copy_request_to_tunnel = no
++            use_tunneled_reply = no
++            virtual_server = "inner-tunnel"
++        }
++...
++    }
++
++to enable EAP-TTLS.
++
++If you want the acceptor be able to identify the user, the RADIUS
++server needs to echo back the EAP username from the inner tunnel;
++for privacy, mech_eap only sends the realm in the EAP Identity
++response. To configure this with FreeRADIUS, add:
++
++    update outer.reply {
++        User-Name = "%{request:User-Name}"
++    }
++
++If you want to add a SAML assertion, do this with "update reply"
++in $prefix/etc/raddb/sites-available/default:
++
++    update reply {
++        SAML-AAA-Assertion = '<saml:Assertion ...'
++        SAML-AAA-Assertion += '...'
++    }
++
++You'll need to split it into multiple lines because of the RADIUS
++attribute size limit.
++
++Testing
++=======
++
++You can then test the MIT or Cyrus GSS and SASL example programs.
++Sample usage is given below. Substitute <user>, <pass> and <host>
++appropriately (<host> is the name of the host running the server,
++not the RADIUS server).
++
++% gss-client -port 5555 -spnego -mech "{1 3 6 1 4 1 5322 22 1 18}" \
++  -user <user>@<realm> -pass <pass> <host> host@<host> \
++  "Testing GSS EAP"
++% gss-server -port 5555 -export host@<host>
++
++Note: for SASL you will be prompted for a username and password.
++
++% client -C -p 5556 -s host -m EAP-AES128 <host>
++% server -c -p 5556 -s host -h <host>
++
++To test fast reauthentication support, add the following to
++/etc/krb5.conf:
++
++[appdefaults]
++        eap_gss = {
++                reauth_use_ccache = TRUE
++        }
++
++This will store a Kerberos ticket for a GSS-EAP authenticated user
++in a credentials cache, which can then be used for re-authentication
++to the same acceptor. You must have a valid keytab configured.
++
++In this testing phase of Moonshot, it's also possible to store a
++default identity and credential in a file. The format consists of
++the string representation of the initiator identity and the password,
++separated by newlines. The default location of this file is
++.gss_eap_id in the user's home directory, however the GSSEAP_IDENTITY
++environment variable can be used to set an alternate location.
++
++You can also set a default realm in [appdefaults]; the Kerberos
++default realm is never used by mech_eap (or at least, that is the
++intention), so if unspecified you must always qualify names. It should
++generally not be necessary to specify this.
++
+diff --git a/mech_eap/README.samba4 b/mech_eap/README.samba4
+new file mode 100644
+index 0000000..d0a94d1
+--- /dev/null
++++ b/mech_eap/README.samba4
+@@ -0,0 +1,52 @@
++Notes on using Moonshot with Samba4. Replace paths as appropriate.
++
++Samba
++-----
++
++* Download Samba4 and apply patches for mechanism agnosticism which are
++  available at http://www.padl.com/~lukeh/samba/
++* Join Samba as a member server or domain controller (only tested former)
++* Extract local service principal key to keytab (currently there do not
++  appear to be tools to do this, but you can get the cleartext password
++  from /usr/local/samba/private/secrets.ldb)
++
++Shibboleth
++----------
++
++* Add a mapping from the PAC RADIUS attribute to urn:mspac: in the file
++  /usr/local/etc/shibboleth/attribute-map.xml:
++
++  <GSSAPIAttribute name="urn:ietf:params:gss-eap:radius-avp urn:x-radius:1679163525"
++                   id="urn:mspac:" binary="true"/>
++
++FreeRADIUS
++----------
++
++Install the rlm_mspac module and configure per below.
++
++* Install dictionary.ukerna so MS-Windows-Auth-Data is defined
++* Create /usr/local/etc/raddb/modules/mspac with the following:
++
++    mspac {
++        keytab = /etc/krb5.keytab
++        spn = host/host.fqdn@KERBEROS.REALM
++    }       
++
++* Add mspac to instantiate stanza in radiusd.conf
++* Add mspac to post-auth stanza in sites-enabled/inner-tunnel
++
++You will need to have a TGT for the host service principal before starting
++radiusd. It's easiest to do this with kinit -k.
++
++Testing
++-------
++
++The Samba server doesn't require any specific command line arguments, although
++on OS X it was necessary to start it with -M single to function under gdb.
++
++For the client, the GSS EAP mechanism can be specified on the command line:
++
++smbclient --password samba --mechanism 1.3.6.1.4.1.5322.22.1.18 '\\host\share'".
++
++There is no Moonshot SSPI implementation as yet, so it is not possible to test
++with a Windows client.
+diff --git a/mech_eap/TODO b/mech_eap/TODO
+new file mode 100644
+index 0000000..0111459
+--- /dev/null
++++ b/mech_eap/TODO
+@@ -0,0 +1,6 @@
++- integration with initiator-side EAP channel bindings
++- investigate initiator-side credential locking
++- always intern OIDs so they never need to be freed
++- handle many-to-many Shibboleth attribute mappings; need to encode both attribute and value index into more
++- add --with-xerces option
++- proper acquire_cred_ext implementation pending specification
+diff --git a/mech_eap/accept_sec_context.c b/mech_eap/accept_sec_context.c
+new file mode 100644
+index 0000000..b089bae
+--- /dev/null
++++ b/mech_eap/accept_sec_context.c
+@@ -0,0 +1,1072 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Establish a security context on the acceptor (server). These functions
++ * wrap around libradsec and (thus) talk to a RADIUS server or proxy.
++ */
++
++#include "gssapiP_eap.h"
++
++#ifdef GSSEAP_ENABLE_REAUTH
++static OM_uint32
++eapGssSmAcceptGssReauth(OM_uint32 *minor,
++                        gss_cred_id_t cred,
++                        gss_ctx_id_t ctx,
++                        gss_name_t target,
++                        gss_OID mech,
++                        OM_uint32 reqFlags,
++                        OM_uint32 timeReq,
++                        gss_channel_bindings_t chanBindings,
++                        gss_buffer_t inputToken,
++                        gss_buffer_t outputToken,
++                        OM_uint32 *smFlags);
++#endif
++
++/*
++ * Mark an acceptor context as ready for cryptographic operations
++ */
++static OM_uint32
++acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
++{
++    OM_uint32 major, tmpMinor;
++    VALUE_PAIR *vp;
++    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
++
++    /* Cache encryption type derived from selected mechanism OID */
++    major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
++                               &ctx->encryptionType);
++    if (GSS_ERROR(major))
++        return major;
++
++    gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
++
++    major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
++                                  PW_USER_NAME, 0, &vp);
++    if (major == GSS_S_COMPLETE && vp->length) {
++        nameBuf.length = vp->length;
++        nameBuf.value = vp->vp_strvalue;
++    } else {
++        ctx->gssFlags |= GSS_C_ANON_FLAG;
++    }
++
++    major = gssEapImportName(minor, &nameBuf,
++                             (ctx->gssFlags & GSS_C_ANON_FLAG) ?
++                                GSS_C_NT_ANONYMOUS : GSS_C_NT_USER_NAME,
++                             ctx->mechanismUsed,
++                             &ctx->initiatorName);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
++                                  PW_MS_MPPE_SEND_KEY, VENDORPEC_MS, &vp);
++    if (GSS_ERROR(major)) {
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    major = gssEapDeriveRfc3961Key(minor,
++                                   vp->vp_octets,
++                                   vp->length,
++                                   ctx->encryptionType,
++                                   &ctx->rfc3961Key);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
++                                       &ctx->checksumType);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = sequenceInit(minor,
++                         &ctx->seqState, ctx->recvSeq,
++                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
++                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
++                         TRUE);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = gssEapCreateAttrContext(minor, cred, ctx,
++                                    &ctx->initiatorName->attrCtx,
++                                    &ctx->expiryTime);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (ctx->expiryTime != 0 && ctx->expiryTime < time(NULL)) {
++        *minor = GSSEAP_CRED_EXPIRED;
++        return GSS_S_CREDENTIALS_EXPIRED;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++eapGssSmAcceptAcceptorName(OM_uint32 *minor,
++                           gss_cred_id_t cred GSSEAP_UNUSED,
++                           gss_ctx_id_t ctx,
++                           gss_name_t target GSSEAP_UNUSED,
++                           gss_OID mech GSSEAP_UNUSED,
++                           OM_uint32 reqFlags GSSEAP_UNUSED,
++                           OM_uint32 timeReq GSSEAP_UNUSED,
++                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                           gss_buffer_t inputToken GSSEAP_UNUSED,
++                           gss_buffer_t outputToken,
++                           OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    /* XXX TODO import and validate name from inputToken */
++
++    if (ctx->acceptorName != GSS_C_NO_NAME) {
++        /* Send desired target name to acceptor */
++        major = gssEapDisplayName(minor, ctx->acceptorName,
++                                  outputToken, NULL);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++#ifdef GSSEAP_DEBUG
++static OM_uint32
++eapGssSmAcceptVendorInfo(OM_uint32 *minor,
++                         gss_cred_id_t cred GSSEAP_UNUSED,
++                         gss_ctx_id_t ctx GSSEAP_UNUSED,
++                         gss_name_t target GSSEAP_UNUSED,
++                         gss_OID mech GSSEAP_UNUSED,
++                         OM_uint32 reqFlags GSSEAP_UNUSED,
++                         OM_uint32 timeReq GSSEAP_UNUSED,
++                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                         gss_buffer_t inputToken,
++                         gss_buffer_t outputToken GSSEAP_UNUSED,
++                         OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    fprintf(stderr, "GSS-EAP: vendor: %.*s\n",
++            (int)inputToken->length, (char *)inputToken->value);
++
++    *minor = 0;
++    return GSS_S_CONTINUE_NEEDED;
++}
++#endif
++
++
++/*
++ * Emit a identity EAP request to force the initiator (peer) to identify
++ * itself.
++ */
++static OM_uint32
++eapGssSmAcceptIdentity(OM_uint32 *minor,
++                       gss_cred_id_t cred,
++                       gss_ctx_id_t ctx,
++                       gss_name_t target GSSEAP_UNUSED,
++                       gss_OID mech GSSEAP_UNUSED,
++                       OM_uint32 reqFlags GSSEAP_UNUSED,
++                       OM_uint32 timeReq GSSEAP_UNUSED,
++                       gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                       gss_buffer_t inputToken,
++                       gss_buffer_t outputToken,
++                       OM_uint32 *smFlags)
++{
++    OM_uint32 major;
++    struct wpabuf *reqData;
++    gss_buffer_desc pktBuffer;
++
++    if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
++        *minor = GSSEAP_CRED_MECH_MISMATCH;
++        return GSS_S_BAD_MECH;
++    }
++
++    if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
++        *minor = GSSEAP_WRONG_SIZE;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    reqData = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, 0,
++                            EAP_CODE_REQUEST, 0);
++    if (reqData == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    pktBuffer.length = wpabuf_len(reqData);
++    pktBuffer.value = (void *)wpabuf_head(reqData);
++
++    major = duplicateBuffer(minor, &pktBuffer, outputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    wpabuf_free(reqData);
++
++    GSSEAP_SM_TRANSITION_NEXT(ctx);
++
++    *minor = 0;
++    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++/*
++ * Returns TRUE if the input token contains an EAP identity response.
++ */
++static int
++isIdentityResponseP(gss_buffer_t inputToken)
++{
++    struct wpabuf respData;
++
++    wpabuf_set(&respData, inputToken->value, inputToken->length);
++
++    return (eap_get_type(&respData) == EAP_TYPE_IDENTITY);
++}
++
++/*
++ * Save the asserted initiator identity from the EAP identity response.
++ */
++static OM_uint32
++importInitiatorIdentity(OM_uint32 *minor,
++                        gss_ctx_id_t ctx,
++                        gss_buffer_t inputToken)
++{
++    OM_uint32 tmpMinor;
++    struct wpabuf respData;
++    const unsigned char *pos;
++    size_t len;
++    gss_buffer_desc nameBuf;
++
++    wpabuf_set(&respData, inputToken->value, inputToken->length);
++
++    pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
++                           &respData, &len);
++    if (pos == NULL) {
++        *minor = GSSEAP_PEER_BAD_MESSAGE;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    nameBuf.value = (void *)pos;
++    nameBuf.length = len;
++
++    gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
++
++    return gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
++                            ctx->mechanismUsed, &ctx->initiatorName);
++}
++
++/*
++ * Pass the asserted initiator identity to the authentication server.
++ */
++static OM_uint32
++setInitiatorIdentity(OM_uint32 *minor,
++                     gss_ctx_id_t ctx,
++                     VALUE_PAIR **vps)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc nameBuf;
++
++    /*
++     * We should have got an EAP identity response, but if we didn't, then
++     * we will just avoid sending User-Name. Note that radsecproxy requires
++     * User-Name to be sent on every request (presumably so it can remain
++     * stateless).
++     */
++    if (ctx->initiatorName != GSS_C_NO_NAME) {
++        major = gssEapDisplayName(minor, ctx->initiatorName, &nameBuf, NULL);
++        if (GSS_ERROR(major))
++            return major;
++
++        major = gssEapRadiusAddAvp(minor, vps, PW_USER_NAME, 0, &nameBuf);
++        if (GSS_ERROR(major))
++            return major;
++
++        gss_release_buffer(&tmpMinor, &nameBuf);
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/*
++ * Pass the asserted acceptor identity to the authentication server.
++ */
++static OM_uint32
++setAcceptorIdentity(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    VALUE_PAIR **vps)
++{
++    OM_uint32 major;
++    gss_buffer_desc nameBuf;
++    krb5_context krbContext = NULL;
++    krb5_principal krbPrinc;
++    struct rs_context *rc = ctx->acceptorCtx.radContext;
++
++    GSSEAP_ASSERT(rc != NULL);
++
++    if (ctx->acceptorName == GSS_C_NO_NAME) {
++        *minor = 0;
++        return GSS_S_COMPLETE;
++    }
++
++    if ((ctx->acceptorName->flags & NAME_FLAG_SERVICE) == 0) {
++        *minor = GSSEAP_BAD_SERVICE_NAME;
++        return GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    krbPrinc = ctx->acceptorName->krbPrincipal;
++    GSSEAP_ASSERT(krbPrinc != NULL);
++    GSSEAP_ASSERT(KRB_PRINC_LENGTH(krbPrinc) >= 2);
++
++    /* Acceptor-Service-Name */
++    krbPrincComponentToGssBuffer(krbPrinc, 0, &nameBuf);
++
++    major = gssEapRadiusAddAvp(minor, vps,
++                               PW_GSS_ACCEPTOR_SERVICE_NAME,
++                               VENDORPEC_UKERNA,
++                               &nameBuf);
++    if (GSS_ERROR(major))
++        return major;
++
++    /* Acceptor-Host-Name */
++    krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf);
++
++    major = gssEapRadiusAddAvp(minor, vps,
++                               PW_GSS_ACCEPTOR_HOST_NAME,
++                               VENDORPEC_UKERNA,
++                               &nameBuf);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
++        /* Acceptor-Service-Specific */
++        krb5_principal_data ssiPrinc = *krbPrinc;
++        char *ssi;
++
++        KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
++        KRB_PRINC_NAME(&ssiPrinc) += 2;
++
++        *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc,
++                                         KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
++        if (*minor != 0)
++            return GSS_S_FAILURE;
++
++        nameBuf.value = ssi;
++        nameBuf.length = strlen(ssi);
++
++        major = gssEapRadiusAddAvp(minor, vps,
++                                   PW_GSS_ACCEPTOR_SERVICE_SPECIFIC,
++                                   VENDORPEC_UKERNA,
++                                   &nameBuf);
++
++        if (GSS_ERROR(major)) {
++            krb5_free_unparsed_name(krbContext, ssi);
++            return major;
++        }
++        krb5_free_unparsed_name(krbContext, ssi);
++    }
++
++    krbPrincRealmToGssBuffer(krbPrinc, &nameBuf);
++    if (nameBuf.length != 0) {
++        /* Acceptor-Realm-Name */
++        major = gssEapRadiusAddAvp(minor, vps,
++                                   PW_GSS_ACCEPTOR_REALM_NAME,
++                                   VENDORPEC_UKERNA,
++                                   &nameBuf);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/*
++ * Allocate a RadSec handle
++ */
++static OM_uint32
++createRadiusHandle(OM_uint32 *minor,
++                   gss_cred_id_t cred,
++                   gss_ctx_id_t ctx)
++{
++    struct gss_eap_acceptor_ctx *actx = &ctx->acceptorCtx;
++    struct rs_error *err;
++    const char *configStanza = "gss-eap";
++    OM_uint32 major;
++
++    GSSEAP_ASSERT(actx->radContext == NULL);
++    GSSEAP_ASSERT(actx->radConn == NULL);
++    GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
++
++    major = gssEapCreateRadiusContext(minor, cred, &actx->radContext);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (cred->radiusConfigStanza.value != NULL)
++        configStanza = (const char *)cred->radiusConfigStanza.value;
++
++    if (rs_conn_create(actx->radContext, &actx->radConn, configStanza) != 0) {
++        err = rs_err_conn_pop(actx->radConn);
++        return gssEapRadiusMapError(minor, err);
++    }
++
++    if (actx->radServer != NULL) {
++        if (rs_conn_select_peer(actx->radConn, actx->radServer) != 0) {
++            err = rs_err_conn_pop(actx->radConn);
++            return gssEapRadiusMapError(minor, err);
++        }
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/*
++ * Process a EAP response from the initiator.
++ */
++static OM_uint32
++eapGssSmAcceptAuthenticate(OM_uint32 *minor,
++                           gss_cred_id_t cred,
++                           gss_ctx_id_t ctx,
++                           gss_name_t target GSSEAP_UNUSED,
++                           gss_OID mech GSSEAP_UNUSED,
++                           OM_uint32 reqFlags GSSEAP_UNUSED,
++                           OM_uint32 timeReq GSSEAP_UNUSED,
++                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                           gss_buffer_t inputToken,
++                           gss_buffer_t outputToken,
++                           OM_uint32 *smFlags)
++{
++    OM_uint32 major, tmpMinor;
++    struct rs_connection *rconn;
++    struct rs_request *request = NULL;
++    struct rs_packet *req = NULL, *resp = NULL;
++    struct radius_packet *frreq, *frresp;
++
++    if (ctx->acceptorCtx.radContext == NULL) {
++        /* May be NULL from an imported partial context */
++        major = createRadiusHandle(minor, cred, ctx);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (isIdentityResponseP(inputToken)) {
++        major = importInitiatorIdentity(minor, ctx, inputToken);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    rconn = ctx->acceptorCtx.radConn;
++
++    if (rs_packet_create_authn_request(rconn, &req, NULL, NULL) != 0) {
++        major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
++        goto cleanup;
++    }
++    frreq = rs_packet_frpkt(req);
++
++    major = setInitiatorIdentity(minor, ctx, &frreq->vps);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = setAcceptorIdentity(minor, ctx, &frreq->vps);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapRadiusAddAvp(minor, &frreq->vps,
++                               PW_EAP_MESSAGE, 0, inputToken);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (ctx->acceptorCtx.state.length != 0) {
++        major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0,
++                                   &ctx->acceptorCtx.state);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
++    }
++
++    if (rs_request_create(rconn, &request) != 0) {
++        major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
++        goto cleanup;
++    }
++
++    rs_request_add_reqpkt(request, req);
++    req = NULL;
++
++    if (rs_request_send(request, &resp) != 0) {
++        major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
++        goto cleanup;
++    }
++
++    GSSEAP_ASSERT(resp != NULL);
++
++    frresp = rs_packet_frpkt(resp);
++    switch (frresp->code) {
++    case PW_ACCESS_CHALLENGE:
++    case PW_AUTHENTICATION_ACK:
++        break;
++    case PW_AUTHENTICATION_REJECT:
++        *minor = GSSEAP_RADIUS_AUTH_FAILURE;
++        major = GSS_S_DEFECTIVE_CREDENTIAL;
++        goto cleanup;
++        break;
++    default:
++        *minor = GSSEAP_UNKNOWN_RADIUS_CODE;
++        major = GSS_S_FAILURE;
++        goto cleanup;
++        break;
++    }
++
++    major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0,
++                               outputToken, TRUE);
++    if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) {
++        *minor = GSSEAP_MISSING_EAP_REQUEST;
++        major = GSS_S_DEFECTIVE_TOKEN;
++        goto cleanup;
++    } else if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (frresp->code == PW_ACCESS_CHALLENGE) {
++        major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0,
++                                   &ctx->acceptorCtx.state, TRUE);
++        if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
++            goto cleanup;
++    } else {
++        ctx->acceptorCtx.vps = frresp->vps;
++        frresp->vps = NULL;
++
++        major = acceptReadyEap(minor, ctx, cred);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        GSSEAP_SM_TRANSITION_NEXT(ctx);
++    }
++
++    major = GSS_S_CONTINUE_NEEDED;
++    *minor = 0;
++    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
++
++cleanup:
++    if (request != NULL)
++        rs_request_destroy(request);
++    if (req != NULL)
++        rs_packet_destroy(req);
++    if (resp != NULL)
++        rs_packet_destroy(resp);
++    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIATOR_EXTS) {
++        GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
++
++        rs_conn_destroy(ctx->acceptorCtx.radConn);
++        ctx->acceptorCtx.radConn = NULL;
++    }
++
++    return major;
++}
++
++static OM_uint32
++eapGssSmAcceptGssFlags(OM_uint32 *minor,
++                       gss_cred_id_t cred GSSEAP_UNUSED,
++                       gss_ctx_id_t ctx,
++                       gss_name_t target GSSEAP_UNUSED,
++                       gss_OID mech GSSEAP_UNUSED,
++                       OM_uint32 reqFlags GSSEAP_UNUSED,
++                       OM_uint32 timeReq GSSEAP_UNUSED,
++                       gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                       gss_buffer_t inputToken,
++                       gss_buffer_t outputToken GSSEAP_UNUSED,
++                       OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    unsigned char *p;
++    OM_uint32 initiatorGssFlags;
++
++    GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
++
++    if (inputToken->length < 4) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    /* allow flags to grow for future expansion */
++    p = (unsigned char *)inputToken->value + inputToken->length - 4;
++
++    initiatorGssFlags = load_uint32_be(p);
++    initiatorGssFlags &= GSSEAP_WIRE_FLAGS_MASK;
++
++    ctx->gssFlags |= initiatorGssFlags;
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++static OM_uint32
++eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
++                                 gss_cred_id_t cred GSSEAP_UNUSED,
++                                 gss_ctx_id_t ctx,
++                                 gss_name_t target GSSEAP_UNUSED,
++                                 gss_OID mech GSSEAP_UNUSED,
++                                 OM_uint32 reqFlags GSSEAP_UNUSED,
++                                 OM_uint32 timeReq GSSEAP_UNUSED,
++                                 gss_channel_bindings_t chanBindings,
++                                 gss_buffer_t inputToken,
++                                 gss_buffer_t outputToken GSSEAP_UNUSED,
++                                 OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++    gss_iov_buffer_desc iov[2];
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
++    iov[0].buffer.length = 0;
++    iov[0].buffer.value = NULL;
++
++    iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM | GSS_IOV_BUFFER_FLAG_ALLOCATED;
++
++    /* XXX necessary because decrypted in place and we verify it later */
++    major = duplicateBuffer(minor, inputToken, &iov[1].buffer);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
++                                    iov, 2, TOK_TYPE_WRAP);
++    if (GSS_ERROR(major)) {
++        gssEapReleaseIov(iov, 2);
++        return major;
++    }
++
++    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
++        !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
++        major = GSS_S_BAD_BINDINGS;
++        *minor = GSSEAP_BINDINGS_MISMATCH;
++    } else {
++        major = GSS_S_CONTINUE_NEEDED;
++        *minor = 0;
++    }
++
++    gssEapReleaseIov(iov, 2);
++
++    return major;
++}
++
++static OM_uint32
++eapGssSmAcceptInitiatorMIC(OM_uint32 *minor,
++                           gss_cred_id_t cred GSSEAP_UNUSED,
++                           gss_ctx_id_t ctx,
++                           gss_name_t target GSSEAP_UNUSED,
++                           gss_OID mech GSSEAP_UNUSED,
++                           OM_uint32 reqFlags GSSEAP_UNUSED,
++                           OM_uint32 timeReq GSSEAP_UNUSED,
++                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                           gss_buffer_t inputToken,
++                           gss_buffer_t outputToken GSSEAP_UNUSED,
++                           OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    GSSEAP_SM_TRANSITION_NEXT(ctx);
++
++    *minor = 0;
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++#ifdef GSSEAP_ENABLE_REAUTH
++static OM_uint32
++eapGssSmAcceptReauthCreds(OM_uint32 *minor,
++                          gss_cred_id_t cred,
++                          gss_ctx_id_t ctx,
++                          gss_name_t target GSSEAP_UNUSED,
++                          gss_OID mech GSSEAP_UNUSED,
++                          OM_uint32 reqFlags GSSEAP_UNUSED,
++                          OM_uint32 timeReq GSSEAP_UNUSED,
++                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                          gss_buffer_t inputToken GSSEAP_UNUSED,
++                          gss_buffer_t outputToken,
++                          OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    /*
++     * If we're built with fast reauthentication enabled, then
++     * fabricate a ticket from the initiator to ourselves.
++     */
++    major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
++    if (major == GSS_S_UNAVAILABLE)
++        major = GSS_S_COMPLETE;
++    if (major == GSS_S_COMPLETE)
++        major = GSS_S_CONTINUE_NEEDED;
++
++    return major;
++}
++#endif
++
++static OM_uint32
++eapGssSmAcceptAcceptorMIC(OM_uint32 *minor,
++                          gss_cred_id_t cred GSSEAP_UNUSED,
++                          gss_ctx_id_t ctx,
++                          gss_name_t target GSSEAP_UNUSED,
++                          gss_OID mech GSSEAP_UNUSED,
++                          OM_uint32 reqFlags GSSEAP_UNUSED,
++                          OM_uint32 timeReq GSSEAP_UNUSED,
++                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                          gss_buffer_t inputToken GSSEAP_UNUSED,
++                          gss_buffer_t outputToken,
++                          OM_uint32 *smFlags)
++{
++    OM_uint32 major;
++
++    major = gssEapMakeTokenMIC(minor, ctx, outputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
++
++    *minor = 0;
++    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
++
++    return GSS_S_COMPLETE;
++}
++
++static struct gss_eap_sm eapGssAcceptorSm[] = {
++    {
++        ITOK_TYPE_ACCEPTOR_NAME_REQ,
++        ITOK_TYPE_ACCEPTOR_NAME_RESP,
++        GSSEAP_STATE_INITIAL,
++        0,
++        eapGssSmAcceptAcceptorName
++    },
++#ifdef GSSEAP_DEBUG
++    {
++        ITOK_TYPE_VENDOR_INFO,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_INITIAL,
++        0,
++        eapGssSmAcceptVendorInfo,
++    },
++#endif
++#ifdef GSSEAP_ENABLE_REAUTH
++    {
++        ITOK_TYPE_REAUTH_REQ,
++        ITOK_TYPE_REAUTH_RESP,
++        GSSEAP_STATE_INITIAL,
++        0,
++        eapGssSmAcceptGssReauth,
++    },
++#endif
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_EAP_REQ,
++        GSSEAP_STATE_INITIAL,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmAcceptIdentity,
++    },
++    {
++        ITOK_TYPE_EAP_RESP,
++        ITOK_TYPE_EAP_REQ,
++        GSSEAP_STATE_AUTHENTICATE,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmAcceptAuthenticate
++    },
++    {
++        ITOK_TYPE_GSS_FLAGS,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_INITIATOR_EXTS,
++        0,
++        eapGssSmAcceptGssFlags
++    },
++    {
++        ITOK_TYPE_GSS_CHANNEL_BINDINGS,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_INITIATOR_EXTS,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmAcceptGssChannelBindings,
++    },
++    {
++        ITOK_TYPE_INITIATOR_MIC,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_INITIATOR_EXTS,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmAcceptInitiatorMIC,
++    },
++#ifdef GSSEAP_ENABLE_REAUTH
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_REAUTH_CREDS,
++        GSSEAP_STATE_ACCEPTOR_EXTS,
++        0,
++        eapGssSmAcceptReauthCreds,
++    },
++#endif
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_ACCEPTOR_MIC,
++        GSSEAP_STATE_ACCEPTOR_EXTS,
++        0,
++        eapGssSmAcceptAcceptorMIC
++    },
++};
++
++OM_uint32
++gssEapAcceptSecContext(OM_uint32 *minor,
++                       gss_ctx_id_t ctx,
++                       gss_cred_id_t cred,
++                       gss_buffer_t input_token,
++                       gss_channel_bindings_t input_chan_bindings,
++                       gss_name_t *src_name,
++                       gss_OID *mech_type,
++                       gss_buffer_t output_token,
++                       OM_uint32 *ret_flags,
++                       OM_uint32 *time_rec,
++                       gss_cred_id_t *delegated_cred_handle)
++{
++    OM_uint32 major, tmpMinor;
++
++    if (cred == GSS_C_NO_CREDENTIAL) {
++        if (ctx->cred == GSS_C_NO_CREDENTIAL) {
++            major = gssEapAcquireCred(minor,
++                                      GSS_C_NO_NAME,
++                                      GSS_C_INDEFINITE,
++                                      GSS_C_NO_OID_SET,
++                                      GSS_C_ACCEPT,
++                                      &ctx->cred,
++                                      NULL,
++                                      NULL);
++            if (GSS_ERROR(major))
++                goto cleanup;
++        }
++
++        cred = ctx->cred;
++    }
++
++    /*
++     * Previously we acquired the credential mutex here, but it should not be
++     * necessary as the acceptor does not access any mutable elements of the
++     * credential handle.
++     */
++
++    /*
++     * Calling gssEapInquireCred() forces the default acceptor credential name
++     * to be resolved.
++     */
++    major = gssEapInquireCred(minor, cred, &ctx->acceptorName, NULL, NULL, NULL);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapSmStep(minor,
++                         cred,
++                         ctx,
++                         GSS_C_NO_NAME,
++                         GSS_C_NO_OID,
++                         0,
++                         GSS_C_INDEFINITE,
++                         input_chan_bindings,
++                         input_token,
++                         output_token,
++                         eapGssAcceptorSm,
++                         sizeof(eapGssAcceptorSm) / sizeof(eapGssAcceptorSm[0]));
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (mech_type != NULL) {
++        OM_uint32 tmpMajor;
++
++        tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, mech_type);
++        if (GSS_ERROR(tmpMajor)) {
++            major = tmpMajor;
++            *minor = tmpMinor;
++            goto cleanup;
++        }
++    }
++    if (ret_flags != NULL)
++        *ret_flags = ctx->gssFlags;
++    if (delegated_cred_handle != NULL)
++        *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
++
++    if (major == GSS_S_COMPLETE) {
++        if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
++            major = gssEapDuplicateName(&tmpMinor, ctx->initiatorName, src_name);
++            if (GSS_ERROR(major))
++                goto cleanup;
++        }
++        if (time_rec != NULL) {
++            major = gssEapContextTime(&tmpMinor, ctx, time_rec);
++            if (GSS_ERROR(major))
++                goto cleanup;
++        }
++    }
++
++    GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
++
++cleanup:
++    return major;
++}
++
++#ifdef GSSEAP_ENABLE_REAUTH
++static OM_uint32
++acceptReadyKrb(OM_uint32 *minor,
++               gss_ctx_id_t ctx,
++               gss_cred_id_t cred,
++               const gss_name_t initiator,
++               const gss_OID mech,
++               OM_uint32 timeRec)
++{
++    OM_uint32 major;
++
++    major = gssEapGlueToMechName(minor, ctx, initiator, &ctx->initiatorName);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = gssEapReauthComplete(minor, ctx, cred, mech, timeRec);
++    if (GSS_ERROR(major))
++        return major;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++eapGssSmAcceptGssReauth(OM_uint32 *minor,
++                        gss_cred_id_t cred,
++                        gss_ctx_id_t ctx,
++                        gss_name_t target GSSEAP_UNUSED,
++                        gss_OID mech,
++                        OM_uint32 reqFlags GSSEAP_UNUSED,
++                        OM_uint32 timeReq GSSEAP_UNUSED,
++                        gss_channel_bindings_t chanBindings,
++                        gss_buffer_t inputToken,
++                        gss_buffer_t outputToken,
++                        OM_uint32 *smFlags)
++{
++    OM_uint32 major, tmpMinor;
++    gss_name_t krbInitiator = GSS_C_NO_NAME;
++    OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
++
++    /*
++     * If we're built with fast reauthentication support, it's valid
++     * for an initiator to send a GSS reauthentication token as its
++     * initial context token, causing us to short-circuit the state
++     * machine and process Kerberos GSS messages instead.
++     */
++
++    ctx->flags |= CTX_FLAG_KRB_REAUTH;
++
++    major = gssAcceptSecContext(minor,
++                                &ctx->reauthCtx,
++                                cred->reauthCred,
++                                inputToken,
++                                chanBindings,
++                                &krbInitiator,
++                                &mech,
++                                outputToken,
++                                &gssFlags,
++                                &timeRec,
++                                NULL);
++    if (major == GSS_S_COMPLETE) {
++        major = acceptReadyKrb(minor, ctx, cred,
++                               krbInitiator, mech, timeRec);
++        if (major == GSS_S_COMPLETE) {
++            GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
++        }
++        ctx->gssFlags = gssFlags;
++    } else if (GSS_ERROR(major) &&
++        (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
++        /* pretend reauthentication attempt never happened */
++        gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
++        ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
++        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
++        major = GSS_S_CONTINUE_NEEDED;
++    }
++
++    gssReleaseName(&tmpMinor, &krbInitiator);
++
++    return major;
++}
++#endif /* GSSEAP_ENABLE_REAUTH */
++
++OM_uint32 GSSAPI_CALLCONV
++gss_accept_sec_context(OM_uint32 *minor,
++                       gss_ctx_id_t *context_handle,
++                       gss_cred_id_t cred,
++                       gss_buffer_t input_token,
++                       gss_channel_bindings_t input_chan_bindings,
++                       gss_name_t *src_name,
++                       gss_OID *mech_type,
++                       gss_buffer_t output_token,
++                       OM_uint32 *ret_flags,
++                       OM_uint32 *time_rec,
++                       gss_cred_id_t *delegated_cred_handle)
++{
++    OM_uint32 major, tmpMinor;
++    gss_ctx_id_t ctx = *context_handle;
++
++    *minor = 0;
++
++    output_token->length = 0;
++    output_token->value = NULL;
++
++    if (src_name != NULL)
++        *src_name = GSS_C_NO_NAME;
++
++    if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        major = gssEapAllocContext(minor, &ctx);
++        if (GSS_ERROR(major))
++            return major;
++
++        *context_handle = ctx;
++    }
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    major = gssEapAcceptSecContext(minor,
++                                   ctx,
++                                   cred,
++                                   input_token,
++                                   input_chan_bindings,
++                                   src_name,
++                                   mech_type,
++                                   output_token,
++                                   ret_flags,
++                                   time_rec,
++                                   delegated_cred_handle);
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    if (GSS_ERROR(major))
++        gssEapReleaseContext(&tmpMinor, context_handle);
++
++    return major;
++}
+diff --git a/mech_eap/acquire_cred.c b/mech_eap/acquire_cred.c
+new file mode 100644
+index 0000000..ae2648e
+--- /dev/null
++++ b/mech_eap/acquire_cred.c
+@@ -0,0 +1,52 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Wrapper for acquiring a credential handle.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_acquire_cred(OM_uint32 *minor,
++                 gss_name_t desired_name,
++                 OM_uint32 time_req,
++                 gss_OID_set desired_mechs,
++                 gss_cred_usage_t cred_usage,
++                 gss_cred_id_t *output_cred_handle,
++                 gss_OID_set *actual_mechs,
++                 OM_uint32 *time_rec)
++{
++    return gssEapAcquireCred(minor, desired_name,
++                             time_req, desired_mechs, cred_usage,
++                             output_cred_handle, actual_mechs, time_rec);
++}
+diff --git a/mech_eap/acquire_cred_with_password.c b/mech_eap/acquire_cred_with_password.c
+new file mode 100644
+index 0000000..8e08358
+--- /dev/null
++++ b/mech_eap/acquire_cred_with_password.c
+@@ -0,0 +1,67 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Wrapper for acquiring a credential handle using a password.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gssspi_acquire_cred_with_password(OM_uint32 *minor,
++                                  const gss_name_t desired_name,
++                                  const gss_buffer_t password,
++                                  OM_uint32 time_req,
++                                  const gss_OID_set desired_mechs,
++                                  gss_cred_usage_t cred_usage,
++                                  gss_cred_id_t *output_cred_handle,
++                                  gss_OID_set *actual_mechs,
++                                  OM_uint32 *time_rec)
++{
++    OM_uint32 major, tmpMinor;
++
++    major = gssEapAcquireCred(minor, desired_name,
++                              time_req, desired_mechs, cred_usage,
++                              output_cred_handle, actual_mechs, time_rec);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapSetCredPassword(minor, *output_cred_handle, password);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gssEapReleaseCred(&tmpMinor, output_cred_handle);
++
++    return major;
++}
+diff --git a/mech_eap/add_cred.c b/mech_eap/add_cred.c
+new file mode 100644
+index 0000000..64d97c0
+--- /dev/null
++++ b/mech_eap/add_cred.c
+@@ -0,0 +1,87 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Wrapper for acquiring a credential handle.
++ */
++
++#include "gssapiP_eap.h"
++
++/*
++ * Note that this shouldn't really be required to be implemented by anything
++ * apart from the mechanism glue layer. However, Heimdal does call into the
++ * mechanism here.
++ */
++OM_uint32 GSSAPI_CALLCONV
++gss_add_cred(OM_uint32 *minor,
++             gss_cred_id_t input_cred_handle GSSEAP_UNUSED,
++             gss_name_t desired_name,
++             gss_OID desired_mech,
++             gss_cred_usage_t cred_usage,
++             OM_uint32 initiator_time_req,
++             OM_uint32 acceptor_time_req,
++             gss_cred_id_t *output_cred_handle,
++             gss_OID_set *actual_mechs,
++             OM_uint32 *initiator_time_rec,
++             OM_uint32 *acceptor_time_rec)
++{
++    OM_uint32 major;
++    OM_uint32 time_req, time_rec = 0;
++    gss_OID_set_desc mechs;
++
++    *minor = 0;
++    *output_cred_handle = GSS_C_NO_CREDENTIAL;
++
++    if (cred_usage == GSS_C_ACCEPT)
++        time_req = acceptor_time_req;
++    else
++        time_req = initiator_time_req;
++
++    mechs.count = 1;
++    mechs.elements = desired_mech;
++
++    major = gssEapAcquireCred(minor,
++                              desired_name,
++                              time_req,
++                              &mechs,
++                              cred_usage,
++                              output_cred_handle,
++                              actual_mechs,
++                              &time_rec);
++
++    if (initiator_time_rec != NULL)
++        *initiator_time_rec = time_rec;
++    if (acceptor_time_rec != NULL)
++        *acceptor_time_rec = time_rec;
++
++    return major;
++}
+diff --git a/mech_eap/add_cred_with_password.c b/mech_eap/add_cred_with_password.c
+new file mode 100644
+index 0000000..b982f0d
+--- /dev/null
++++ b/mech_eap/add_cred_with_password.c
+@@ -0,0 +1,93 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Wrapper for acquiring a credential handle using a password.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_add_cred_with_password(OM_uint32 *minor,
++                           const gss_cred_id_t input_cred_handle GSSEAP_UNUSED,
++                           const gss_name_t desired_name,
++                           const gss_OID desired_mech,
++                           const gss_buffer_t password,
++                           gss_cred_usage_t cred_usage,
++                           OM_uint32 initiator_time_req,
++                           OM_uint32 acceptor_time_req,
++                           gss_cred_id_t *output_cred_handle,
++                           gss_OID_set *actual_mechs,
++                           OM_uint32 *initiator_time_rec,
++                           OM_uint32 *acceptor_time_rec)
++{
++    OM_uint32 major, tmpMinor;
++    OM_uint32 time_req, time_rec = 0;
++    gss_OID_set_desc mechs;
++
++    *minor = 0;
++    *output_cred_handle = GSS_C_NO_CREDENTIAL;
++
++    if (cred_usage == GSS_C_ACCEPT)
++        time_req = acceptor_time_req;
++    else
++        time_req = initiator_time_req;
++
++    mechs.count = 1;
++    mechs.elements = desired_mech;
++
++    major = gssEapAcquireCred(minor,
++                              desired_name,
++                              time_req,
++                              &mechs,
++                              cred_usage,
++                              output_cred_handle,
++                              actual_mechs,
++                              &time_rec);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapSetCredPassword(minor, *output_cred_handle, password);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (initiator_time_rec != NULL)
++        *initiator_time_rec = time_rec;
++    if (acceptor_time_rec != NULL)
++        *acceptor_time_rec = time_rec;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gssEapReleaseCred(&tmpMinor, output_cred_handle);
++
++    return major;
++}
+diff --git a/mech_eap/authdata_plugin.h b/mech_eap/authdata_plugin.h
+new file mode 100644
+index 0000000..32bff2f
+--- /dev/null
++++ b/mech_eap/authdata_plugin.h
+@@ -0,0 +1,331 @@
++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++ * krb5/authdata_plugin.h
++ *
++ * Copyright (C) 2007 Apple Inc.  All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ *
++ * AuthorizationData plugin definitions for Kerberos 5.
++ */
++
++/*
++ * This is considered an INTERNAL interface at this time.
++ *
++ * Some work is needed before exporting it:
++ *
++ * + Documentation.
++ * + Sample code.
++ * + Test cases (preferably automated testing under "make check").
++ * + Hook into TGS exchange too; will change API.
++ * + Examine memory management issues, especially for Windows; may
++ *   change API.
++ *
++ * Other changes that would be nice to have, but not necessarily
++ * before making this interface public:
++ *
++ * + Library support for AD-IF-RELEVANT and similar wrappers.  (We can
++ *   make the plugin construct them if it wants them.)
++ * + KDC could combine/optimize wrapped AD elements provided by
++ *   multiple plugins, e.g., two IF-RELEVANT sequences could be
++ *   merged.  (The preauth plugin API also has this bug, we're going
++ *   to need a general fix.)
++ */
++
++#ifndef KRB5_AUTHDATA_PLUGIN_H_INCLUDED
++#define KRB5_AUTHDATA_PLUGIN_H_INCLUDED
++#include <krb5/krb5.h>
++
++/*
++ * While arguments of these types are passed-in, for the most part a
++ * authorization data module can treat them as opaque.  If we need
++ * keying data, we can ask for it directly.
++ */
++struct _krb5_db_entry_new;
++
++/*
++ * The function table / structure which an authdata server module must export as
++ * "authdata_server_0".  NOTE: replace "0" with "1" for the type and
++ * variable names if this gets picked up by upstream.  If the interfaces work
++ * correctly, future versions of the table will add either more callbacks or
++ * more arguments to callbacks, and in both cases we'll be able to wrap the v0
++ * functions.
++ */
++/* extern krb5plugin_authdata_ftable_v0 authdata_server_0; */
++typedef struct krb5plugin_authdata_server_ftable_v0 {
++    /* Not-usually-visible name. */
++    char *name;
++
++    /*
++     * Per-plugin initialization/cleanup.  The init function is called
++     * by the KDC when the plugin is loaded, and the fini function is
++     * called before the plugin is unloaded.  Both are optional.
++     */
++    krb5_error_code (*init_proc)(krb5_context, void **);
++    void (*fini_proc)(krb5_context, void *);
++    /*
++     * Actual authorization data handling function.  If this field
++     * holds a null pointer, this mechanism will be skipped, and the
++     * init/fini functions will not be run.
++     *
++     * This function should only modify the field
++     * enc_tkt_reply->authorization_data.  All other values should be
++     * considered inputs only.  And, it should *modify* the field, not
++     * overwrite it and assume that there are no other authdata
++     * plugins in use.
++     *
++     * Memory management: authorization_data is a malloc-allocated,
++     * null-terminated sequence of malloc-allocated pointers to
++     * authorization data structures.  This plugin code currently
++     * assumes the libraries, KDC, and plugin all use the same malloc
++     * pool, which may be a problem if/when we get the KDC code
++     * running on Windows.
++     *
++     * If this function returns a non-zero error code, a message
++     * is logged, but no other action is taken.  Other authdata
++     * plugins will be called, and a response will be sent to the
++     * client (barring other problems).
++     */
++    krb5_error_code (*authdata_proc)(krb5_context,
++                                     struct _krb5_db_entry_new *client,
++                                     krb5_data *req_pkt,
++                                     krb5_kdc_req *request,
++                                     krb5_enc_tkt_part *enc_tkt_reply);
++} krb5plugin_server_authdata_ftable_v0;
++
++typedef krb5plugin_server_authdata_ftable_v0 krb5plugin_authdata_ftable_v0;
++
++typedef struct krb5plugin_authdata_server_ftable_v2 {
++    /* Not-usually-visible name. */
++    char *name;
++
++    /*
++     * Per-plugin initialization/cleanup.  The init function is called
++     * by the KDC when the plugin is loaded, and the fini function is
++     * called before the plugin is unloaded.  Both are optional.
++     */
++    krb5_error_code (*init_proc)(krb5_context, void **);
++    void (*fini_proc)(krb5_context, void *);
++    /*
++     * Actual authorization data handling function.  If this field
++     * holds a null pointer, this mechanism will be skipped, and the
++     * init/fini functions will not be run.
++     *
++     * This function should only modify the field
++     * enc_tkt_reply->authorization_data.  All other values should be
++     * considered inputs only.  And, it should *modify* the field, not
++     * overwrite it and assume that there are no other authdata
++     * plugins in use.
++     *
++     * Memory management: authorization_data is a malloc-allocated,
++     * null-terminated sequence of malloc-allocated pointers to
++     * authorization data structures.  This plugin code currently
++     * assumes the libraries, KDC, and plugin all use the same malloc
++     * pool, which may be a problem if/when we get the KDC code
++     * running on Windows.
++     *
++     * If this function returns a non-zero error code, a message
++     * is logged, but no other action is taken.  Other authdata
++     * plugins will be called, and a response will be sent to the
++     * client (barring other problems).
++     */
++    krb5_error_code (*authdata_proc)(krb5_context,
++                                     unsigned int flags,
++                                     struct _krb5_db_entry_new *client,
++                                     struct _krb5_db_entry_new *server,
++                                     struct _krb5_db_entry_new *tgs,
++                                     krb5_keyblock *client_key,
++                                     krb5_keyblock *server_key,
++                                     krb5_keyblock *tgs_key,
++                                     krb5_data *req_pkt,
++                                     krb5_kdc_req *request,
++                                     krb5_const_principal for_user_princ,
++                                     krb5_enc_tkt_part *enc_tkt_request,
++                                     krb5_enc_tkt_part *enc_tkt_reply);
++} krb5plugin_authdata_server_ftable_v2;
++
++typedef krb5plugin_authdata_server_ftable_v2 krb5plugin_authdata_ftable_v2;
++
++typedef krb5_error_code
++(*authdata_client_plugin_init_proc)(krb5_context context,
++                                    void **plugin_context);
++
++#define AD_USAGE_AS_REQ         0x01
++#define AD_USAGE_TGS_REQ        0x02
++#define AD_USAGE_AP_REQ         0x04
++#define AD_USAGE_KDC_ISSUED     0x08
++#define AD_USAGE_MASK           0x0F
++#define AD_INFORMATIONAL        0x10
++
++struct _krb5_authdata_context;
++
++typedef void
++(*authdata_client_plugin_flags_proc)(krb5_context kcontext,
++                                     void *plugin_context,
++                                     krb5_authdatatype ad_type,
++                                     krb5_flags *flags);
++
++typedef void
++(*authdata_client_plugin_fini_proc)(krb5_context kcontext,
++                                    void *plugin_context);
++
++typedef krb5_error_code
++(*authdata_client_request_init_proc)(krb5_context kcontext,
++                                     struct _krb5_authdata_context *context,
++                                     void *plugin_context,
++                                     void **request_context);
++
++typedef void
++(*authdata_client_request_fini_proc)(krb5_context kcontext,
++                                     struct _krb5_authdata_context *context,
++                                     void *plugin_context,
++                                     void *request_context);
++
++typedef krb5_error_code
++(*authdata_client_import_authdata_proc)(krb5_context kcontext,
++                                        struct _krb5_authdata_context *context,
++                                        void *plugin_context,
++                                        void *request_context,
++                                        krb5_authdata **authdata,
++                                        krb5_boolean kdc_issued_flag,
++                                        krb5_const_principal issuer);
++
++typedef krb5_error_code
++(*authdata_client_export_authdata_proc)(krb5_context kcontext,
++                                        struct _krb5_authdata_context *context,
++                                        void *plugin_context,
++                                        void *request_context,
++                                        krb5_flags usage,
++                                        krb5_authdata ***authdata);
++
++typedef krb5_error_code
++(*authdata_client_get_attribute_types_proc)(krb5_context kcontext,
++                                            struct _krb5_authdata_context *context,
++                                            void *plugin_context,
++                                            void *request_context,
++                                            krb5_data **attrs);
++
++typedef krb5_error_code
++(*authdata_client_get_attribute_proc)(krb5_context kcontext,
++                                      struct _krb5_authdata_context *context,
++                                      void *plugin_context,
++                                      void *request_context,
++                                      const krb5_data *attribute,
++                                      krb5_boolean *authenticated,
++                                      krb5_boolean *complete,
++                                      krb5_data *value,
++                                      krb5_data *display_value,
++                                      int *more);
++
++typedef krb5_error_code
++(*authdata_client_set_attribute_proc)(krb5_context kcontext,
++                                      struct _krb5_authdata_context *context,
++                                      void *plugin_context,
++                                      void *request_context,
++                                      krb5_boolean complete,
++                                      const krb5_data *attribute,
++                                      const krb5_data *value);
++
++typedef krb5_error_code
++(*authdata_client_delete_attribute_proc)(krb5_context kcontext,
++                                         struct _krb5_authdata_context *context,
++                                         void *plugin_context,
++                                         void *request_context,
++                                         const krb5_data *attribute);
++
++typedef krb5_error_code
++(*authdata_client_export_internal_proc)(krb5_context kcontext,
++                                        struct _krb5_authdata_context *context,
++                                        void *plugin_context,
++                                        void *request_context,
++                                        krb5_boolean restrict_authenticated,
++                                        void **ptr);
++
++typedef void
++(*authdata_client_free_internal_proc)(krb5_context kcontext,
++                                      struct _krb5_authdata_context *context,
++                                      void *plugin_context,
++                                      void *request_context,
++                                      void *ptr);
++
++typedef krb5_error_code
++(*authdata_client_verify_proc)(krb5_context kcontext,
++                               struct _krb5_authdata_context *context,
++                               void *plugin_context,
++                               void *request_context,
++                               const krb5_auth_context *auth_context,
++                               const krb5_keyblock *key,
++                               const krb5_ap_req *req);
++
++typedef krb5_error_code
++(*authdata_client_size_proc)(krb5_context kcontext,
++                             struct _krb5_authdata_context *context,
++                             void *plugin_context,
++                             void *request_context,
++                             size_t *sizep);
++
++typedef krb5_error_code
++(*authdata_client_externalize_proc)(krb5_context kcontext,
++                                    struct _krb5_authdata_context *context,
++                                    void *plugin_context,
++                                    void *request_context,
++                                    krb5_octet **buffer,
++                                    size_t *lenremain);
++
++typedef krb5_error_code
++(*authdata_client_internalize_proc)(krb5_context kcontext,
++                                    struct _krb5_authdata_context *context,
++                                    void *plugin_context,
++                                    void *request_context,
++                                    krb5_octet **buffer,
++                                    size_t *lenremain);
++
++typedef krb5_error_code
++(*authdata_client_copy_proc)(krb5_context kcontext,
++                             struct _krb5_authdata_context *context,
++                             void *plugin_context,
++                             void *request_context,
++                             void *dst_plugin_context,
++                             void *dst_request_context);
++
++typedef struct krb5plugin_authdata_client_ftable_v0 {
++    char *name;
++    krb5_authdatatype *ad_type_list;
++    authdata_client_plugin_init_proc init;
++    authdata_client_plugin_fini_proc fini;
++    authdata_client_plugin_flags_proc flags;
++    authdata_client_request_init_proc request_init;
++    authdata_client_request_fini_proc request_fini;
++    authdata_client_get_attribute_types_proc get_attribute_types;
++    authdata_client_get_attribute_proc get_attribute;
++    authdata_client_set_attribute_proc set_attribute;
++    authdata_client_delete_attribute_proc delete_attribute;
++    authdata_client_export_authdata_proc export_authdata;
++    authdata_client_import_authdata_proc import_authdata;
++    authdata_client_export_internal_proc export_internal;
++    authdata_client_free_internal_proc free_internal;
++    authdata_client_verify_proc verify;
++    authdata_client_size_proc size;
++    authdata_client_externalize_proc externalize;
++    authdata_client_internalize_proc internalize;
++    authdata_client_copy_proc copy; /* optional */
++} krb5plugin_authdata_client_ftable_v0;
++
++#endif /* KRB5_AUTHDATA_PLUGIN_H_INCLUDED */
+diff --git a/mech_eap/authorize_localname.c b/mech_eap/authorize_localname.c
+new file mode 100644
+index 0000000..0037e2b
+--- /dev/null
++++ b/mech_eap/authorize_localname.c
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Local authorization services.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gssspi_authorize_localname(OM_uint32 *minor,
++                           const gss_name_t name GSSEAP_UNUSED,
++                           gss_const_buffer_t local_user GSSEAP_UNUSED,
++                           gss_const_OID local_nametype GSSEAP_UNUSED)
++{
++    /*
++     * The MIT mechglue will fallback to comparing names in the absence
++     * of a mechanism implementation of gss_userok. To avoid this and
++     * force the mechglue to use attribute-based authorization, always
++     * return access denied here.
++     */
++
++    *minor = 0;
++    return GSS_S_UNAUTHORIZED;
++}
+diff --git a/mech_eap/canonicalize_name.c b/mech_eap/canonicalize_name.c
+new file mode 100644
+index 0000000..5e66798
+--- /dev/null
++++ b/mech_eap/canonicalize_name.c
+@@ -0,0 +1,64 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Function for canonicalizing a name; presently just duplicates it.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_canonicalize_name(OM_uint32 *minor,
++                      const gss_name_t input_name,
++                      const gss_OID mech_type,
++                      gss_name_t *output_name)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (!gssEapIsMechanismOid(mech_type))
++        return GSS_S_BAD_MECH;
++
++    if (input_name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&input_name->mutex);
++
++    major = gssEapCanonicalizeName(minor, input_name, mech_type, output_name);
++
++    GSSEAP_MUTEX_UNLOCK(&input_name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/compare_name.c b/mech_eap/compare_name.c
+new file mode 100644
+index 0000000..edadf3e
+--- /dev/null
++++ b/mech_eap/compare_name.c
+@@ -0,0 +1,46 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Compare two names.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_compare_name(OM_uint32 *minor,
++                 gss_name_t name1,
++                 gss_name_t name2,
++                 int *name_equal)
++{
++    return gssEapCompareName(minor, name1, name2, name_equal);
++}
+diff --git a/mech_eap/context_time.c b/mech_eap/context_time.c
+new file mode 100644
+index 0000000..ae47d6c
+--- /dev/null
++++ b/mech_eap/context_time.c
+@@ -0,0 +1,69 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Determine remaining lifetime of a context handle.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_context_time(OM_uint32 *minor,
++                 gss_ctx_id_t ctx,
++                 OM_uint32 *time_rec)
++{
++    OM_uint32 major;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        major = GSS_S_NO_CONTEXT;
++        goto cleanup;
++    }
++
++    major = gssEapContextTime(minor, ctx, time_rec);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/delete_name_attribute.c b/mech_eap/delete_name_attribute.c
+new file mode 100644
+index 0000000..fe0ff8f
+--- /dev/null
++++ b/mech_eap/delete_name_attribute.c
+@@ -0,0 +1,60 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Wrapper for removing a name attribute.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_delete_name_attribute(OM_uint32 *minor,
++                          gss_name_t name,
++                          gss_buffer_t attr)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&name->mutex);
++
++    major = gssEapDeleteNameAttribute(minor, name, attr);
++
++    GSSEAP_MUTEX_UNLOCK(&name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/delete_sec_context.c b/mech_eap/delete_sec_context.c
+new file mode 100644
+index 0000000..7913e45
+--- /dev/null
++++ b/mech_eap/delete_sec_context.c
+@@ -0,0 +1,81 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Release a context handle.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_delete_sec_context(OM_uint32 *minor,
++                       gss_ctx_id_t *context_handle,
++                       gss_buffer_t output_token)
++{
++    OM_uint32 major;
++    gss_ctx_id_t ctx = *context_handle;
++
++    *minor = 0;
++
++    if (output_token != GSS_C_NO_BUFFER) {
++        output_token->length = 0;
++        output_token->value = NULL;
++    }
++
++    if (ctx == GSS_C_NO_CONTEXT)
++        return GSS_S_COMPLETE;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (output_token != GSS_C_NO_BUFFER) {
++        gss_iov_buffer_desc iov[2];
++
++        iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
++        iov[0].buffer.value = NULL;
++        iov[0].buffer.length = 0;
++
++        iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
++        iov[1].buffer.value = NULL;
++        iov[1].buffer.length = 0;
++
++        major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
++                                   iov, 2, TOK_TYPE_DELETE_CONTEXT);
++        if (GSS_ERROR(major)) {
++            GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++            return major;
++        }
++    }
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return gssEapReleaseContext(minor, context_handle);
++}
+diff --git a/mech_eap/dictionary.ukerna b/mech_eap/dictionary.ukerna
+new file mode 100644
+index 0000000..0e35d43
+--- /dev/null
++++ b/mech_eap/dictionary.ukerna
+@@ -0,0 +1,20 @@
++# -*- text -*-
++#
++#     GSS-EAP VSAs
++#
++#     $Id$
++#
++
++VENDOR        UKERNA                          25622
++
++BEGIN-VENDOR UKERNA
++
++ATTRIBUTE     GSS-Acceptor-Service-Name       128     string
++ATTRIBUTE     GSS-Acceptor-Host-Name          129     string
++ATTRIBUTE     GSS-Acceptor-Service-Specific   130     string
++ATTRIBUTE     GSS-Acceptor-Realm-Name         131     string
++ATTRIBUTE     SAML-AAA-Assertion              132     string
++ATTRIBUTE     MS-Windows-Auth-Data            133     octets
++ATTRIBUTE     MS-Windows-Group-Sid            134     string
++
++END-VENDOR UKERNA
+diff --git a/mech_eap/display_name.c b/mech_eap/display_name.c
+new file mode 100644
+index 0000000..2d87e66
+--- /dev/null
++++ b/mech_eap/display_name.c
+@@ -0,0 +1,48 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Wrapper for "displaying" (returning string representation of) a name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_display_name(OM_uint32 *minor,
++                 gss_name_t name,
++                 gss_buffer_t output_name_buffer,
++                 gss_OID *output_name_type)
++{
++    /* Lock not required as long as attributes are not used */
++    return gssEapDisplayName(minor, name, output_name_buffer,
++                             output_name_type);
++}
+diff --git a/mech_eap/display_name_ext.c b/mech_eap/display_name_ext.c
+new file mode 100644
+index 0000000..d6791d4
+--- /dev/null
++++ b/mech_eap/display_name_ext.c
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Parameterized version of gss_display_name(), currently unimplemented.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_display_name_ext(OM_uint32 *minor,
++                     gss_name_t name GSSEAP_UNUSED,
++                     gss_OID display_as_name_type GSSEAP_UNUSED,
++                     gss_buffer_t display_name)
++{
++    *minor = 0;
++
++    display_name->length = 0;
++    display_name->value = NULL;
++
++    return GSS_S_UNAVAILABLE;
++}
+diff --git a/mech_eap/display_status.c b/mech_eap/display_status.c
+new file mode 100644
+index 0000000..fc0d1ab
+--- /dev/null
++++ b/mech_eap/display_status.c
+@@ -0,0 +1,203 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Function for converting mechanism error codes to strings.
++ */
++
++#include "gssapiP_eap.h"
++
++struct gss_eap_status_info {
++    OM_uint32 code;
++    char *message;
++    struct gss_eap_status_info *next;
++};
++
++void
++gssEapDestroyStatusInfo(struct gss_eap_status_info *p)
++{
++    struct gss_eap_status_info *next;
++
++    for (; p != NULL; p = next) {
++        next = p->next;
++        GSSEAP_FREE(p->message);
++        GSSEAP_FREE(p);
++    }
++}
++
++/*
++ * Associate a message with a mechanism (minor) status code. This function
++ * takes ownership of the message regardless of success. The message must
++ * be explicitly cleared, if required, so it is suggested that a specific
++ * minor code is either always or never associated with a message, to avoid
++ * dangling (and potentially confusing) error messages.
++ */
++static void
++saveStatusInfoNoCopy(OM_uint32 minor, char *message)
++{
++    struct gss_eap_status_info **next = NULL, *p = NULL;
++    struct gss_eap_thread_local_data *tld = gssEapGetThreadLocalData();
++
++    if (tld != NULL) {
++        for (p = tld->statusInfo; p != NULL; p = p->next) {
++            if (p->code == minor) {
++                /* Set message in-place */
++                if (p->message != NULL)
++                    GSSEAP_FREE(p->message);
++                p->message = message;
++                return;
++            }
++            next = &p->next;
++        }
++        p = GSSEAP_CALLOC(1, sizeof(*p));
++    }
++
++    if (p == NULL) {
++        if (message != NULL)
++            GSSEAP_FREE(message);
++        return;
++    }
++
++    p->code = minor;
++    p->message = message;
++
++    if (next != NULL)
++        *next = p;
++    else
++        tld->statusInfo = p;
++}
++
++static const char *
++getStatusInfo(OM_uint32 minor)
++{
++    struct gss_eap_status_info *p;
++    struct gss_eap_thread_local_data *tld = gssEapGetThreadLocalData();
++
++    if (tld != NULL) {
++        for (p = tld->statusInfo; p != NULL; p = p->next) {
++            if (p->code == minor)
++                return p->message;
++        }
++    }
++    return NULL;
++}
++
++void
++gssEapSaveStatusInfo(OM_uint32 minor, const char *format, ...)
++{
++#ifdef WIN32
++    OM_uint32 tmpMajor, tmpMinor;
++    char buf[BUFSIZ];
++    gss_buffer_desc s = GSS_C_EMPTY_BUFFER;
++    va_list ap;
++
++    if (format != NULL) {
++        va_start(ap, format);
++        snprintf(buf, sizeof(buf), format, ap);
++        va_end(ap);
++    }
++
++    tmpMajor = makeStringBuffer(&tmpMinor, buf, &s);
++    if (!GSS_ERROR(tmpMajor))
++        saveStatusInfoNoCopy(minor, (char *)s.value);
++#else
++    char *s = NULL;
++    int n;
++    va_list ap;
++
++    if (format != NULL) {
++        va_start(ap, format);
++        n = vasprintf(&s, format, ap);
++        if (n == -1)
++            s = NULL;
++        va_end(ap);
++    }
++
++    saveStatusInfoNoCopy(minor, s);
++#endif /* WIN32 */
++}
++
++OM_uint32
++gssEapDisplayStatus(OM_uint32 *minor,
++                    OM_uint32 status_value,
++                    gss_buffer_t status_string)
++{
++    OM_uint32 major;
++    krb5_context krbContext = NULL;
++    const char *errMsg;
++
++    status_string->length = 0;
++    status_string->value = NULL;
++
++    errMsg = getStatusInfo(status_value);
++    if (errMsg == NULL) {
++        GSSEAP_KRB_INIT(&krbContext);
++
++        /* Try the com_err message */
++        errMsg = krb5_get_error_message(krbContext, status_value);
++    }
++
++    if (errMsg != NULL) {
++        major = makeStringBuffer(minor, errMsg, status_string);
++    } else {
++        major = GSS_S_COMPLETE;
++        *minor = 0;
++    }
++
++    if (krbContext != NULL)
++        krb5_free_error_message(krbContext, errMsg);
++
++    return major;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_display_status(OM_uint32 *minor,
++                   OM_uint32 status_value,
++                   int status_type,
++                   gss_OID mech_type,
++                   OM_uint32 *message_context,
++                   gss_buffer_t status_string)
++{
++    if (!gssEapIsMechanismOid(mech_type)) {
++        *minor = GSSEAP_WRONG_MECH;
++        return GSS_S_BAD_MECH;
++    }
++
++    if (status_type != GSS_C_MECH_CODE ||
++        *message_context != 0) {
++        /* we rely on the mechglue for GSS_C_GSS_CODE */
++        *minor = 0;
++        return GSS_S_BAD_STATUS;
++    }
++
++    return gssEapDisplayStatus(minor, status_value, status_string);
++}
+diff --git a/mech_eap/duplicate_name.c b/mech_eap/duplicate_name.c
+new file mode 100644
+index 0000000..303619e
+--- /dev/null
++++ b/mech_eap/duplicate_name.c
+@@ -0,0 +1,60 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Duplicate a name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_duplicate_name(OM_uint32 *minor,
++                   const gss_name_t input_name,
++                   gss_name_t *dest_name)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (input_name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&input_name->mutex);
++
++    major = gssEapDuplicateName(minor, input_name, dest_name);
++
++    GSSEAP_MUTEX_UNLOCK(&input_name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/eap_mech.c b/mech_eap/eap_mech.c
+new file mode 100644
+index 0000000..96e00c2
+--- /dev/null
++++ b/mech_eap/eap_mech.c
+@@ -0,0 +1,219 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Initialisation and finalise functions.
++ */
++
++#include "gssapiP_eap.h"
++
++static OM_uint32
++eapPeerRegisterMethods(OM_uint32 *minor)
++{
++    OM_uint32 ret = 0;
++
++#ifdef EAP_MD5
++    if (ret == 0)
++        ret = eap_peer_md5_register();
++#endif /* EAP_MD5 */
++
++#ifdef EAP_TLS
++    if (ret == 0)
++        ret = eap_peer_tls_register();
++#endif /* EAP_TLS */
++
++#ifdef EAP_MSCHAPv2
++    if (ret == 0)
++        ret = eap_peer_mschapv2_register();
++#endif /* EAP_MSCHAPv2 */
++
++#ifdef EAP_PEAP
++    if (ret == 0)
++        ret = eap_peer_peap_register();
++#endif /* EAP_PEAP */
++
++#ifdef EAP_TTLS
++    if (ret == 0)
++        ret = eap_peer_ttls_register();
++#endif /* EAP_TTLS */
++
++#ifdef EAP_GTC
++    if (ret == 0)
++        ret = eap_peer_gtc_register();
++#endif /* EAP_GTC */
++
++#ifdef EAP_OTP
++    if (ret == 0)
++        ret = eap_peer_otp_register();
++#endif /* EAP_OTP */
++
++#ifdef EAP_SIM
++    if (ret == 0)
++        ret = eap_peer_sim_register();
++#endif /* EAP_SIM */
++
++#ifdef EAP_LEAP
++    if (ret == 0)
++        ret = eap_peer_leap_register();
++#endif /* EAP_LEAP */
++
++#ifdef EAP_PSK
++    if (ret == 0)
++        ret = eap_peer_psk_register();
++#endif /* EAP_PSK */
++
++#ifdef EAP_AKA
++    if (ret == 0)
++        ret = eap_peer_aka_register();
++#endif /* EAP_AKA */
++
++#ifdef EAP_AKA_PRIME
++    if (ret == 0)
++        ret = eap_peer_aka_prime_register();
++#endif /* EAP_AKA_PRIME */
++
++#ifdef EAP_FAST
++    if (ret == 0)
++        ret = eap_peer_fast_register();
++#endif /* EAP_FAST */
++
++#ifdef EAP_PAX
++    if (ret == 0)
++        ret = eap_peer_pax_register();
++#endif /* EAP_PAX */
++
++#ifdef EAP_SAKE
++    if (ret == 0)
++        ret = eap_peer_sake_register();
++#endif /* EAP_SAKE */
++
++#ifdef EAP_GPSK
++    if (ret == 0)
++        ret = eap_peer_gpsk_register();
++#endif /* EAP_GPSK */
++
++#ifdef EAP_WSC
++    if (ret == 0)
++        ret = eap_peer_wsc_register();
++#endif /* EAP_WSC */
++
++#ifdef EAP_IKEV2
++    if (ret == 0)
++        ret = eap_peer_ikev2_register();
++#endif /* EAP_IKEV2 */
++
++#ifdef EAP_VENDOR_TEST
++    if (ret == 0)
++        ret = eap_peer_vendor_test_register();
++#endif /* EAP_VENDOR_TEST */
++
++#ifdef EAP_TNC
++    if (ret == 0)
++        ret = eap_peer_tnc_register();
++#endif /* EAP_TNC */
++
++    if (ret == 0)
++        return GSS_S_COMPLETE;
++
++    *minor = GSSEAP_LIBEAP_INIT_FAILURE;
++    return GSS_S_FAILURE;
++}
++
++static OM_uint32
++gssEapInitLibEap(OM_uint32 *minor)
++{
++    return eapPeerRegisterMethods(minor);
++}
++
++static OM_uint32
++gssEapInitLibRadsec(OM_uint32 *minor)
++{
++    if (0) {
++        *minor = GSSEAP_RADSEC_INIT_FAILURE;
++        return GSS_S_FAILURE;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++void gssEapFinalize(void) GSSEAP_DESTRUCTOR;
++
++OM_uint32
++gssEapInitiatorInit(OM_uint32 *minor)
++{
++    OM_uint32 major;
++
++    initialize_eapg_error_table();
++    initialize_rse_error_table();
++
++    major = gssEapInitLibEap(minor);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = gssEapInitLibRadsec(minor);
++    if (GSS_ERROR(major))
++        return major;
++
++#ifdef GSSEAP_ENABLE_REAUTH
++    major = gssEapReauthInitialize(minor);
++    if (GSS_ERROR(major))
++        return major;
++#endif
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++void
++gssEapFinalize(void)
++{
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    OM_uint32 minor;
++
++    gssEapAttrProvidersFinalize(&minor);
++#endif
++    eap_peer_unregister_methods();
++}
++
++#ifdef GSSEAP_CONSTRUCTOR
++static void gssEapInitiatorInitAssert(void) GSSEAP_CONSTRUCTOR;
++
++static void
++gssEapInitiatorInitAssert(void)
++{
++    OM_uint32 major, minor;
++
++    major = gssEapInitiatorInit(&minor);
++
++    GSSEAP_ASSERT(!GSS_ERROR(major));
++}
++#endif
+diff --git a/mech_eap/exchange_meta_data.c b/mech_eap/exchange_meta_data.c
+new file mode 100644
+index 0000000..5d56795
+--- /dev/null
++++ b/mech_eap/exchange_meta_data.c
+@@ -0,0 +1,82 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ *
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gssEapExchangeMetaData(OM_uint32 *minor,
++                       gss_const_OID mech GSSEAP_UNUSED,
++                       gss_cred_id_t cred GSSEAP_UNUSED,
++                       gss_ctx_id_t *ctx GSSEAP_UNUSED,
++                       const gss_name_t name GSSEAP_UNUSED,
++                       OM_uint32 req_flags GSSEAP_UNUSED,
++                       gss_const_buffer_t meta_data GSSEAP_UNUSED)
++{
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_exchange_meta_data(OM_uint32 *minor,
++                       gss_const_OID mech,
++                       gss_cred_id_t cred,
++                       gss_ctx_id_t *context_handle,
++                       const gss_name_t name,
++                       OM_uint32 req_flags,
++                       gss_const_buffer_t meta_data)
++{
++    gss_ctx_id_t ctx = *context_handle;
++    OM_uint32 major;
++
++    if (cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    if (*context_handle != GSS_C_NO_CONTEXT)
++        GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    major = gssEapExchangeMetaData(minor, mech, cred, &ctx,
++                                   name, req_flags, meta_data);
++
++    if (*context_handle != GSS_C_NO_CONTEXT)
++        GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++    else
++        *context_handle = ctx;
++
++    if (cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/export_name.c b/mech_eap/export_name.c
+new file mode 100644
+index 0000000..d91033f
+--- /dev/null
++++ b/mech_eap/export_name.c
+@@ -0,0 +1,60 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Serialise a name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_export_name(OM_uint32 *minor,
++                const gss_name_t input_name,
++                gss_buffer_t exported_name)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (input_name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&input_name->mutex);
++
++    major = gssEapExportName(minor, input_name, exported_name);
++
++    GSSEAP_MUTEX_UNLOCK(&input_name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/export_name_composite.c b/mech_eap/export_name_composite.c
+new file mode 100644
+index 0000000..b2a90ae
+--- /dev/null
++++ b/mech_eap/export_name_composite.c
+@@ -0,0 +1,62 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Serialise a name and its attributes.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_export_name_composite(OM_uint32 *minor,
++                          gss_name_t input_name,
++                          gss_buffer_t exported_name)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (input_name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&input_name->mutex);
++
++    major = gssEapExportNameInternal(minor, input_name, exported_name,
++                                     EXPORT_NAME_FLAG_OID |
++                                     EXPORT_NAME_FLAG_COMPOSITE);
++
++    GSSEAP_MUTEX_UNLOCK(&input_name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/export_sec_context.c b/mech_eap/export_sec_context.c
+new file mode 100644
+index 0000000..e5be6d8
+--- /dev/null
++++ b/mech_eap/export_sec_context.c
+@@ -0,0 +1,246 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Serialise a security context. On the acceptor, this may be partially
++ * established.
++ */
++
++#include "gssapiP_eap.h"
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++static OM_uint32
++gssEapExportPartialContext(OM_uint32 *minor,
++                           gss_ctx_id_t ctx,
++                           gss_buffer_t token)
++{
++    OM_uint32 major, tmpMinor;
++    size_t length, serverLen = 0;
++    unsigned char *p;
++    char serverBuf[MAXHOSTNAMELEN];
++    if (ctx->acceptorCtx.radConn != NULL) {
++        if (rs_conn_get_current_peer(ctx->acceptorCtx.radConn,
++                                     serverBuf, sizeof(serverBuf)) != 0) {
++#if 0
++            return gssEapRadiusMapError(minor,
++                                        rs_err_conn_pop(ctx->acceptorCtx.radConn));
++#else
++            serverBuf[0] = '\0'; /* not implemented yet */
++#endif
++        }
++        serverLen = strlen(serverBuf);
++    }
++    length = 4 + serverLen + 4 + ctx->acceptorCtx.state.length;
++
++    token->value = GSSEAP_MALLOC(length);
++    if (token->value == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++    token->length = length;
++
++    p = (unsigned char *)token->value;
++
++    store_uint32_be(serverLen, p);
++    p += 4;
++    if (serverLen != 0) {
++        memcpy(p, serverBuf, serverLen);
++        p += serverLen;
++    }
++
++    store_uint32_be(ctx->acceptorCtx.state.length, p);
++    p += 4;
++    if (ctx->acceptorCtx.state.length != 0) {
++        memcpy(p, ctx->acceptorCtx.state.value,
++               ctx->acceptorCtx.state.length);
++        p += ctx->acceptorCtx.state.length;
++    }
++
++    GSSEAP_ASSERT(p == (unsigned char *)token->value + token->length);
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gss_release_buffer(&tmpMinor, token);
++
++    return major;
++}
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++
++OM_uint32
++gssEapExportSecContext(OM_uint32 *minor,
++                       gss_ctx_id_t ctx,
++                       gss_buffer_t token)
++{
++    OM_uint32 major, tmpMinor;
++    size_t length;
++    gss_buffer_desc initiatorName = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc acceptorName = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc partialCtx = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc key;
++    unsigned char *p;
++
++    if ((CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) ||
++        ctx->mechanismUsed == GSS_C_NO_OID) {
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        return GSS_S_NO_CONTEXT;
++    }
++
++    key.length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
++    key.value  = KRB_KEY_DATA(&ctx->rfc3961Key);
++
++    if (ctx->initiatorName != GSS_C_NO_NAME) {
++        major = gssEapExportNameInternal(minor, ctx->initiatorName,
++                                         &initiatorName,
++                                         EXPORT_NAME_FLAG_COMPOSITE);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (ctx->acceptorName != GSS_C_NO_NAME) {
++        major = gssEapExportNameInternal(minor, ctx->acceptorName,
++                                         &acceptorName,
++                                         EXPORT_NAME_FLAG_COMPOSITE);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    /*
++     * The partial context is only transmitted for unestablished acceptor
++     * contexts.
++     */
++    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) &&
++        (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
++        major = gssEapExportPartialContext(minor, ctx, &partialCtx);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++#endif
++
++    length  = 16;                               /* version, state, flags, */
++    length += 4 + ctx->mechanismUsed->length;   /* mechanismUsed */
++    length += 12 + key.length;                  /* rfc3961Key.value */
++    length += 4 + initiatorName.length;         /* initiatorName.value */
++    length += 4 + acceptorName.length;          /* acceptorName.value */
++    length += 24 + sequenceSize(ctx->seqState); /* seqState */
++
++    if (partialCtx.value != NULL)
++        length += 4 + partialCtx.length;        /* partialCtx.value */
++
++    token->value = GSSEAP_MALLOC(length);
++    if (token->value == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++    token->length = length;
++
++    p = (unsigned char *)token->value;
++
++    store_uint32_be(EAP_EXPORT_CONTEXT_V1, &p[0]);        /* version */
++    store_uint32_be(GSSEAP_SM_STATE(ctx),  &p[4]);
++    store_uint32_be(ctx->flags,            &p[8]);
++    store_uint32_be(ctx->gssFlags,         &p[12]);
++    p = store_oid(ctx->mechanismUsed,      &p[16]);
++
++    store_uint32_be(ctx->checksumType,     &p[0]);
++    store_uint32_be(ctx->encryptionType,   &p[4]);
++    p = store_buffer(&key,                 &p[8], FALSE);
++
++    p = store_buffer(&initiatorName,       p, FALSE);
++    p = store_buffer(&acceptorName,        p, FALSE);
++
++    store_uint64_be(ctx->expiryTime,       &p[0]);
++    store_uint64_be(ctx->sendSeq,          &p[8]);
++    store_uint64_be(ctx->recvSeq,          &p[16]);
++    p += 24;
++
++    major = sequenceExternalize(minor, ctx->seqState, &p, &length);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (partialCtx.value != NULL)
++        p = store_buffer(&partialCtx, p, FALSE);
++
++    GSSEAP_ASSERT(p == (unsigned char *)token->value + token->length);
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gss_release_buffer(&tmpMinor, token);
++    gss_release_buffer(&tmpMinor, &initiatorName);
++    gss_release_buffer(&tmpMinor, &acceptorName);
++    gss_release_buffer(&tmpMinor, &partialCtx);
++
++    return major;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_export_sec_context(OM_uint32 *minor,
++                       gss_ctx_id_t *context_handle,
++                       gss_buffer_t interprocess_token)
++{
++    OM_uint32 major, tmpMinor;
++    gss_ctx_id_t ctx = *context_handle;
++
++    interprocess_token->length = 0;
++    interprocess_token->value = NULL;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    major = gssEapExportSecContext(minor, ctx, interprocess_token);
++    if (GSS_ERROR(major)) {
++        GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++        return major;
++    }
++
++    *context_handle = GSS_C_NO_CONTEXT;
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    gssEapReleaseContext(&tmpMinor, &ctx);
++
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/get_mic.c b/mech_eap/get_mic.c
+new file mode 100644
+index 0000000..7161e9c
+--- /dev/null
++++ b/mech_eap/get_mic.c
+@@ -0,0 +1,89 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Message protection services: make a message integerity check.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_get_mic(OM_uint32 *minor,
++            gss_ctx_id_t ctx,
++            gss_qop_t qop_req,
++            gss_buffer_t message_buffer,
++            gss_buffer_t message_token)
++{
++    OM_uint32 major;
++    gss_iov_buffer_desc iov[2];
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    if (qop_req != GSS_C_QOP_DEFAULT) {
++        *minor = GSSEAP_UNKNOWN_QOP;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    *minor = 0;
++
++    message_token->value = NULL;
++    message_token->length = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
++    iov[0].buffer = *message_buffer;
++
++    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
++    iov[1].buffer.value = NULL;
++    iov[1].buffer.length = 0;
++
++    major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL, iov, 2, TOK_TYPE_MIC);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    *message_token = iov[1].buffer;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/get_name_attribute.c b/mech_eap/get_name_attribute.c
+new file mode 100644
+index 0000000..a885823
+--- /dev/null
++++ b/mech_eap/get_name_attribute.c
+@@ -0,0 +1,67 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++/*
++ * Wrapper for retrieving a naming attribute.
++ */
++
++OM_uint32 GSSAPI_CALLCONV
++gss_get_name_attribute(OM_uint32 *minor,
++                       gss_name_t name,
++                       gss_buffer_t attr,
++                       int *authenticated,
++                       int *complete,
++                       gss_buffer_t value,
++                       gss_buffer_t display_value,
++                       int *more)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&name->mutex);
++
++    major = gssEapGetNameAttribute(minor, name, attr,
++                                   authenticated, complete,
++                                   value, display_value, more);
++
++    GSSEAP_MUTEX_UNLOCK(&name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/gssapiP_eap.h b/mech_eap/gssapiP_eap.h
+new file mode 100644
+index 0000000..d1790a0
+--- /dev/null
++++ b/mech_eap/gssapiP_eap.h
+@@ -0,0 +1,410 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#ifndef _GSSAPIP_EAP_H_
++#define _GSSAPIP_EAP_H_ 1
++
++#include "config.h"
++
++#ifdef HAVE_HEIMDAL_VERSION
++#define KRB5_DEPRECATED         /* so we can use krb5_free_unparsed_name() */
++#endif
++
++#include <assert.h>
++#include <string.h>
++#include <errno.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef HAVE_STDLIB_H
++#include <stdlib.h>
++#endif
++#ifdef HAVE_STDARG_H
++#include <stdarg.h>
++#endif
++#include <time.h>
++#ifdef HAVE_SYS_PARAM_H
++#include <sys/param.h>
++#endif
++
++#ifdef WIN32
++#ifndef MAXHOSTNAMELEN
++# include <WinSock2.h>
++# define MAXHOSTNAMELEN NI_MAXHOST
++#endif
++#endif
++
++/* GSS headers */
++#include <gssapi/gssapi.h>
++#include <gssapi/gssapi_krb5.h>
++#ifdef HAVE_HEIMDAL_VERSION
++typedef struct gss_any *gss_any_t;
++#else
++#include <gssapi/gssapi_ext.h>
++#endif
++#include "gssapi_eap.h"
++
++#ifndef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH
++typedef const gss_OID_desc *gss_const_OID;
++#endif
++
++/* Kerberos headers */
++#include <krb5.h>
++
++/* EAP headers */
++#include <includes.h>
++#include <common.h>
++#include <eap_peer/eap.h>
++#include <eap_peer/eap_config.h>
++#include <eap_peer/eap_methods.h>
++#include <eap_common/eap_common.h>
++#include <wpabuf.h>
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++/* FreeRADIUS headers */
++#ifdef __cplusplus
++extern "C" {
++#ifndef WIN32
++#define operator fr_operator
++#endif
++#endif
++#include <freeradius/libradius.h>
++#include <freeradius/radius.h>
++
++#undef pid_t
++
++/* libradsec headers */
++#include <radsec/radsec.h>
++#include <radsec/request.h>
++#ifdef __cplusplus
++#ifndef WIN32
++#undef operator
++#endif
++}
++#endif
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++
++#include "gsseap_err.h"
++#include "radsec_err.h"
++#include "util.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* These name flags are informative and not actually used by anything yet */
++#define NAME_FLAG_NAI                       0x00000001
++#define NAME_FLAG_SERVICE                   0x00000002
++#define NAME_FLAG_COMPOSITE                 0x00000004
++
++struct gss_eap_saml_attr_ctx;
++struct gss_eap_attr_ctx;
++
++#ifdef HAVE_HEIMDAL_VERSION
++struct gss_name_t_desc_struct
++#else
++struct gss_name_struct
++#endif
++{
++    GSSEAP_MUTEX mutex; /* mutex protects attrCtx */
++    OM_uint32 flags;
++    gss_OID mechanismUsed; /* this is immutable */
++    krb5_principal krbPrincipal; /* this is immutable */
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    struct gss_eap_attr_ctx *attrCtx;
++#endif
++};
++
++#define CRED_FLAG_INITIATE                  0x00010000
++#define CRED_FLAG_ACCEPT                    0x00020000
++#define CRED_FLAG_PASSWORD                  0x00040000
++#define CRED_FLAG_DEFAULT_CCACHE            0x00080000
++#define CRED_FLAG_RESOLVED                  0x00100000
++#define CRED_FLAG_TARGET                    0x00200000
++#define CRED_FLAG_PUBLIC_MASK               0x0000FFFF
++
++#ifdef HAVE_HEIMDAL_VERSION
++struct gss_cred_id_t_desc_struct
++#else
++struct gss_cred_id_struct
++#endif
++{
++    GSSEAP_MUTEX mutex;
++    OM_uint32 flags;
++    gss_name_t name;
++    gss_name_t target; /* for initiator */
++    gss_buffer_desc password;
++    gss_OID_set mechanisms;
++    time_t expiryTime;
++    gss_buffer_desc radiusConfigFile;
++    gss_buffer_desc radiusConfigStanza;
++    gss_buffer_desc caCertificate;
++    gss_buffer_desc subjectNameConstraint;
++    gss_buffer_desc subjectAltNameConstraint;
++#ifdef GSSEAP_ENABLE_REAUTH
++    krb5_ccache krbCredCache;
++    gss_cred_id_t reauthCred;
++#endif
++};
++
++#define CTX_FLAG_INITIATOR                  0x00000001
++#define CTX_FLAG_KRB_REAUTH                 0x00000002
++
++#define CTX_IS_INITIATOR(ctx)               (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
++
++#define CTX_IS_ESTABLISHED(ctx)             ((ctx)->state == GSSEAP_STATE_ESTABLISHED)
++
++/* Initiator context flags */
++#define CTX_FLAG_EAP_SUCCESS                0x00010000
++#define CTX_FLAG_EAP_RESTART                0x00020000
++#define CTX_FLAG_EAP_FAIL                   0x00040000
++#define CTX_FLAG_EAP_RESP                   0x00080000
++#define CTX_FLAG_EAP_NO_RESP                0x00100000
++#define CTX_FLAG_EAP_REQ                    0x00200000
++#define CTX_FLAG_EAP_PORT_ENABLED           0x00400000
++#define CTX_FLAG_EAP_ALT_ACCEPT             0x00800000
++#define CTX_FLAG_EAP_ALT_REJECT             0x01000000
++#define CTX_FLAG_EAP_MASK                   0xFFFF0000
++
++struct gss_eap_initiator_ctx {
++    unsigned int idleWhile;
++    struct eap_peer_config eapPeerConfig;
++    struct eap_sm *eap;
++    struct wpabuf reqData;
++};
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++struct gss_eap_acceptor_ctx {
++    struct rs_context *radContext;
++    struct rs_connection *radConn;
++    char *radServer;
++    gss_buffer_desc state;
++    VALUE_PAIR *vps;
++};
++#endif
++
++#ifdef HAVE_HEIMDAL_VERSION
++struct gss_ctx_id_t_desc_struct
++#else
++struct gss_ctx_id_struct
++#endif
++{
++    GSSEAP_MUTEX mutex;
++    enum gss_eap_state state;
++    OM_uint32 flags;
++    OM_uint32 gssFlags;
++    gss_OID mechanismUsed;
++    krb5_cksumtype checksumType;
++    krb5_enctype encryptionType;
++    krb5_keyblock rfc3961Key;
++    gss_name_t initiatorName;
++    gss_name_t acceptorName;
++    time_t expiryTime;
++    uint64_t sendSeq, recvSeq;
++    void *seqState;
++    gss_cred_id_t cred;
++    union {
++        struct gss_eap_initiator_ctx initiator;
++        #define initiatorCtx         ctxU.initiator
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++        struct gss_eap_acceptor_ctx  acceptor;
++        #define acceptorCtx          ctxU.acceptor
++#endif
++#ifdef GSSEAP_ENABLE_REAUTH
++        gss_ctx_id_t                 reauth;
++        #define reauthCtx            ctxU.reauth
++#endif
++    } ctxU;
++    const struct gss_eap_token_buffer_set *inputTokens;
++    const struct gss_eap_token_buffer_set *outputTokens;
++};
++
++#define TOK_FLAG_SENDER_IS_ACCEPTOR         0x01
++#define TOK_FLAG_WRAP_CONFIDENTIAL          0x02
++#define TOK_FLAG_ACCEPTOR_SUBKEY            0x04
++
++#define KEY_USAGE_ACCEPTOR_SEAL             22
++#define KEY_USAGE_ACCEPTOR_SIGN             23
++#define KEY_USAGE_INITIATOR_SEAL            24
++#define KEY_USAGE_INITIATOR_SIGN            25
++
++/* accept_sec_context.c */
++OM_uint32
++gssEapAcceptSecContext(OM_uint32 *minor,
++                       gss_ctx_id_t ctx,
++                       gss_cred_id_t cred,
++                       gss_buffer_t input_token,
++                       gss_channel_bindings_t input_chan_bindings,
++                       gss_name_t *src_name,
++                       gss_OID *mech_type,
++                       gss_buffer_t output_token,
++                       OM_uint32 *ret_flags,
++                       OM_uint32 *time_rec,
++                       gss_cred_id_t *delegated_cred_handle);
++
++/* init_sec_context.c */
++OM_uint32
++gssEapInitSecContext(OM_uint32 *minor,
++                     gss_cred_id_t cred,
++                     gss_ctx_id_t ctx,
++                     gss_name_t target_name,
++                     gss_OID mech_type,
++                     OM_uint32 req_flags,
++                     OM_uint32 time_req,
++                     gss_channel_bindings_t input_chan_bindings,
++                     gss_buffer_t input_token,
++                     gss_OID *actual_mech_type,
++                     gss_buffer_t output_token,
++                     OM_uint32 *ret_flags,
++                     OM_uint32 *time_rec);
++
++/* wrap_iov.c */
++OM_uint32
++gssEapWrapOrGetMIC(OM_uint32 *minor,
++                   gss_ctx_id_t ctx,
++                   int conf_req_flag,
++                   int *conf_state,
++                   gss_iov_buffer_desc *iov,
++                   int iov_count,
++                   enum gss_eap_token_type toktype);
++
++OM_uint32
++gssEapUnwrapOrVerifyMIC(OM_uint32 *minor_status,
++                        gss_ctx_id_t ctx,
++                        int *conf_state,
++                        gss_qop_t *qop_state,
++                        gss_iov_buffer_desc *iov,
++                        int iov_count,
++                        enum gss_eap_token_type toktype);
++
++OM_uint32
++gssEapWrapIovLength(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    int conf_req_flag,
++                    gss_qop_t qop_req,
++                    int *conf_state,
++                    gss_iov_buffer_desc *iov,
++                    int iov_count);
++OM_uint32
++gssEapWrap(OM_uint32 *minor,
++           gss_ctx_id_t ctx,
++           int conf_req_flag,
++           gss_qop_t qop_req,
++           gss_buffer_t input_message_buffer,
++           int *conf_state,
++           gss_buffer_t output_message_buffer);
++
++unsigned char
++rfc4121Flags(gss_ctx_id_t ctx, int receiving);
++
++/* display_status.c */
++void
++gssEapSaveStatusInfo(OM_uint32 minor, const char *format, ...);
++
++OM_uint32
++gssEapDisplayStatus(OM_uint32 *minor,
++                    OM_uint32 status_value,
++                    gss_buffer_t status_string);
++
++#define IS_WIRE_ERROR(err)              ((err) > GSSEAP_RESERVED && \
++                                         (err) <= GSSEAP_RADIUS_PROT_FAILURE)
++
++/* upper bound of RADIUS error range must be kept in sync with radsec.h */
++#define IS_RADIUS_ERROR(err)            ((err) >= ERROR_TABLE_BASE_rse && \
++                                         (err) <= ERROR_TABLE_BASE_rse + 20)
++
++/* exchange_meta_data.c */
++OM_uint32 GSSAPI_CALLCONV
++gssEapExchangeMetaData(OM_uint32 *minor,
++                       gss_const_OID mech,
++                       gss_cred_id_t cred,
++                       gss_ctx_id_t *ctx,
++                       const gss_name_t name,
++                       OM_uint32 req_flags,
++                       gss_const_buffer_t meta_data);
++
++/* export_sec_context.c */
++OM_uint32
++gssEapExportSecContext(OM_uint32 *minor,
++                       gss_ctx_id_t ctx,
++                       gss_buffer_t token);
++
++/* import_sec_context.c */
++OM_uint32
++gssEapImportContext(OM_uint32 *minor,
++                    gss_buffer_t token,
++                    gss_ctx_id_t ctx);
++
++/* inquire_sec_context_by_oid.c */
++#define NEGOEX_INITIATOR_SALT      "gss-eap-negoex-initiator"
++#define NEGOEX_INITIATOR_SALT_LEN  (sizeof(NEGOEX_INITIATOR_SALT) - 1)
++
++#define NEGOEX_ACCEPTOR_SALT       "gss-eap-negoex-acceptor"
++#define NEGOEX_ACCEPTOR_SALT_LEN   (sizeof(NEGOEX_ACCEPTOR_SALT) - 1)
++
++/* pseudo_random.c */
++OM_uint32
++gssEapPseudoRandom(OM_uint32 *minor,
++                   gss_ctx_id_t ctx,
++                   int prf_key,
++                   const gss_buffer_t prf_in,
++                   ssize_t desired_output_len,
++                   gss_buffer_t prf_out);
++
++/* query_mechanism_info.c */
++OM_uint32
++gssQueryMechanismInfo(OM_uint32 *minor,
++                      gss_const_OID mech_oid,
++                      unsigned char auth_scheme[16]);
++
++/* query_meta_data.c */
++OM_uint32
++gssEapQueryMetaData(OM_uint32 *minor,
++                    gss_const_OID mech GSSEAP_UNUSED,
++                    gss_cred_id_t cred,
++                    gss_ctx_id_t *context_handle,
++                    const gss_name_t name,
++                    OM_uint32 req_flags GSSEAP_UNUSED,
++                    gss_buffer_t meta_data);
++
++/* eap_mech.c */
++OM_uint32
++gssEapInitiatorInit(OM_uint32 *minor);
++
++void
++gssEapFinalize(void);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _GSSAPIP_EAP_H_ */
+diff --git a/mech_eap/gssapi_eap.h b/mech_eap/gssapi_eap.h
+new file mode 100644
+index 0000000..588665b
+--- /dev/null
++++ b/mech_eap/gssapi_eap.h
+@@ -0,0 +1,90 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#ifndef _GSSAPI_EAP_H_
++#define _GSSAPI_EAP_H_ 1
++
++#include <gssapi/gssapi.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif /* __cplusplus */
++
++/*
++ * GSS EAP mechanism OIDs.
++ */
++extern gss_OID GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM;
++extern gss_OID GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM;
++
++/*
++ * Mechanism name OID.
++ */
++extern gss_OID GSS_EAP_NT_EAP_NAME;
++
++/*
++ * The libradsec configuration file; defaults to radsec.conf
++ * in the system configuration directory if unspecified.
++ */
++extern gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE;
++
++/*
++ * The stanza in the libradsec configuration file; defaults
++ * to "gss-eap" if unspecified.
++ */
++extern gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA;
++
++/*
++ * Flags as a 32-bit integer in network byte order,
++ * followed by a boolean octet indicating whether to
++ * clear the specified flags (if absent, defaults to
++ * FALSE, ie. set flags).
++ */
++extern gss_OID GSS_EAP_CRED_SET_CRED_FLAG;
++
++/*
++ * Password; for mechanism glues that do not support
++ * gss_acquire_cred_with_password(), this can be set
++ * on an existing credentials handle.
++ */
++extern gss_OID GSS_EAP_CRED_SET_CRED_PASSWORD;
++
++/*
++ * Credentials flag indicating the local attributes
++ * processing should be skipped.
++ */
++#define GSS_EAP_DISABLE_LOCAL_ATTRS_FLAG    0x00000001
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif /* _GSSAPI_EAP_H_ */
+diff --git a/mech_eap/gsseap_err.et b/mech_eap/gsseap_err.et
+new file mode 100644
+index 0000000..d60c2c7
+--- /dev/null
++++ b/mech_eap/gsseap_err.et
+@@ -0,0 +1,162 @@
++#
++# Copyright (c) 2011, JANET(UK)
++#  All rights reserved.
++# 
++#  Redistribution and use in source and binary forms, with or without
++#  modification, are permitted provided that the following conditions
++#  are met:
++# 
++#  1. Redistributions of source code must retain the above copyright
++#     notice, this list of conditions and the following disclaimer.
++# 
++#  2. Redistributions in binary form must reproduce the above copyright
++#     notice, this list of conditions and the following disclaimer in the
++#     documentation and/or other materials provided with the distribution.
++# 
++#  3. Neither the name of JANET(UK) nor the names of its contributors
++#     may be used to endorse or promote products derived from this software
++#     without specific prior written permission.
++# 
++#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++#  SUCH DAMAGE.
++#
++
++error_table eapg
++
++#
++# Protocol errors that can be returned in an error token. This should match
++# up with makeErrorToken in accept_sec_context.c.
++#
++error_code GSSEAP_RESERVED,                     ""
++error_code GSSEAP_WRONG_SIZE,                   "Buffer is incorrect size"
++error_code GSSEAP_WRONG_MECH,                   "Mechanism OID is incorrect"
++error_code GSSEAP_BAD_TOK_HEADER,               "Token header is malformed or corrupt"
++error_code GSSEAP_TOK_TRUNC,                    "Token is missing data"
++error_code GSSEAP_BAD_DIRECTION,                "Packet was replayed in wrong direction"
++error_code GSSEAP_WRONG_TOK_ID,                 "Received token ID does not match expected token ID"
++error_code GSSEAP_CRIT_ITOK_UNAVAILABLE,        "Critical inner token type unavailable"
++error_code GSSEAP_MISSING_REQUIRED_ITOK,        "Missing required inner token"
++error_code GSSEAP_DUPLICATE_ITOK,               "Duplicate inner token received"
++error_code GSSEAP_WRONG_ITOK,                   "Recieved invalid inner token for current state"
++error_code GSSEAP_KEY_UNAVAILABLE,              "EAP key unavailable"
++error_code GSSEAP_KEY_TOO_SHORT,                "EAP key too short"
++error_code GSSEAP_RADIUS_AUTH_FAILURE,          "Authentication rejected by RADIUS server"
++error_code GSSEAP_UNKNOWN_RADIUS_CODE,          "Received unknown response code from RADIUS server"
++error_code GSSEAP_MISSING_EAP_REQUEST,          "RADIUS response is missing EAP request"
++error_code GSSEAP_RADIUS_PROT_FAILURE,          "Generic RADIUS failure"
++
++#
++# Context errors
++#
++error_code GSSEAP_CONTEXT_ESTABLISHED,          "Context is already fully established"
++error_code GSSEAP_CONTEXT_INCOMPLETE,           "Attempt to use incomplete security context"
++error_code GSSEAP_BAD_CONTEXT_TOKEN,            "Context token is malformed or corrupt"
++error_code GSSEAP_BAD_ERROR_TOKEN,              "Error token is malformed or corrupt"
++error_code GSSEAP_BAD_CONTEXT_OPTION,           "Bad context option"
++
++#
++# Name errors
++#
++error_code GSSEAP_BAD_SERVICE_NAME,             "Name is not a valid service name"
++error_code GSSEAP_BAD_INITIATOR_NAME,           "Initiator identity must be a valid name"
++error_code GSSEAP_NO_HOSTNAME,                  "Could not determine local host name"
++error_code GSSEAP_NO_ACCEPTOR_NAME,             "Could not determine acceptor identity"
++error_code GSSEAP_BAD_NAME_TOKEN,               "Name token is malformed or corrupt"
++error_code GSSEAP_NO_LOCAL_MAPPING,             "Unable to map name to a local identity"
++
++#
++# Credential errors
++#
++error_code GSSEAP_BAD_USAGE,                    "Credential usage type is unknown"
++error_code GSSEAP_CRED_USAGE_MISMATCH,          "Credential usage does not match requested usage"
++error_code GSSEAP_CRED_MECH_MISMATCH,           "Credential is not usable with this mechanism"
++error_code GSSEAP_CRED_EXPIRED,                 "Attributes indicate credentials have expired"
++error_code GSSEAP_BAD_CRED_OPTION,              "Bad credential option"
++error_code GSSEAP_NO_DEFAULT_IDENTITY,          "Default credentials identity unavailable"
++error_code GSSEAP_NO_DEFAULT_CRED,              "Missing default password or other credentials"
++error_code GSSEAP_CRED_RESOLVED,                "Credential is already fully resolved"
++
++#
++# Local identity service errors
++#
++error_code GSSEAP_UNABLE_TO_START_IDENTITY_SERVICE,     "Unable to start identity service"
++error_code GSSEAP_NO_IDENTITY_SELECTED,                 "No identity selected"
++error_code GSSEAP_IDENTITY_SERVICE_INSTALL_ERROR,       "Identity service installation error"
++error_code GSSEAP_IDENTITY_SERVICE_OS_ERROR,            "Identity service OS error"
++error_code GSSEAP_IDENTITY_SERVICE_IPC_ERROR,           "Identity service IPC error"
++error_code GSSEAP_IDENTITY_SERVICE_UNKNOWN_ERROR,       "Unknown identity service error"
++
++#
++# Wrap/unwrap/PRF errors
++#
++error_code GSSEAP_BAD_WRAP_TOKEN,               "Bad RFC 4121 wrap or MIC token"
++error_code GSSEAP_MISSING_IOV,                  "IOV is missing required buffer"
++error_code GSSEAP_BAD_STREAM_IOV,               "Stream IOV can only contain a single data buffer"
++error_code GSSEAP_BAD_PADDING_IOV,              "Padding IOV is not permitted for RFC 4121 tokens"
++error_code GSSEAP_UNKNOWN_QOP,                  "Unknown quality of protection specified"
++error_code GSSEAP_INPUT_TOO_LONG,               "PRF input too long"
++error_code GSSEAP_BAD_PRF_KEY,                  "PRF key usage type is unknown"
++
++#
++# libeap errors
++#
++error_code GSSEAP_LIBEAP_INIT_FAILURE,          "Failed to initialize EAP library"
++error_code GSSEAP_PEER_SM_INIT_FAILURE,         "Failed to create EAP state machine"
++error_code GSSEAP_PEER_SM_STEP_FAILURE,         "Failed to step EAP state machine"
++error_code GSSEAP_PEER_AUTH_FAILURE,            "EAP peer authentication failure"
++error_code GSSEAP_PEER_BAD_MESSAGE,             "Received bad EAP message"
++
++#
++# RadSec initialisation errors
++#
++error_code GSSEAP_RADSEC_INIT_FAILURE,          "Failed to initialize RadSec library"
++error_code GSSEAP_RADSEC_CONTEXT_FAILURE,       "Failed to create RadSec context"
++
++#
++# Attribute errors
++#
++error_code GSSEAP_NO_ATTR_CONTEXT,              "Name has no attributes"
++error_code GSSEAP_NO_ATTR_PROVIDERS,            "Failed to initialize attribute providers"
++error_code GSSEAP_NO_SUCH_ATTR,                 "Unknown naming attribute"
++error_code GSSEAP_BAD_ATTR_TOKEN,               "Serialised attributes are malformed or corrupt"
++error_code GSSEAP_ATTR_CONTEXT_FAILURE,         "Failed to initialize attribute context"
++
++#
++# OpenSAML errors
++#
++error_code GSSEAP_SAML_INIT_FAILURE,            "Failed to initialize SAML library"
++error_code GSSEAP_SAML_SEC_POLICY_FAILURE,      "Failed to process SAML security policy"
++error_code GSSEAP_SAML_BINDING_FAILURE,         "Failed in SAML binding processing"
++error_code GSSEAP_SAML_PROFILE_FAILURE,         "Failed to process SAML profile"
++error_code GSSEAP_SAML_FATAL_PROFILE_FAILURE,   "Non-recoverable failure in SAML profile processing"
++error_code GSSEAP_SAML_RETRY_PROFILE_FAILURE,   "Temporary failure in SAML profile processing"
++error_code GSSEAP_SAML_METADATA_FAILURE,        "Failure related to SAML metadata use"
++
++#
++# Shibboleth errors
++#
++error_code GSSEAP_SHIB_INIT_FAILURE,            "Failed to initialize Shibboleth"
++error_code GSSEAP_SHIB_ATTR_FAILURE,            "Failure during local attribute processing"
++error_code GSSEAP_SHIB_ATTR_EXTRACT_FAILURE,    "Failed to extract local attributes"
++error_code GSSEAP_SHIB_ATTR_FILTER_FAILURE,     "Failed to filter local attributes"
++error_code GSSEAP_SHIB_ATTR_RESOLVE_FAILURE,    "Failed to resolve local attributes"
++error_code GSSEAP_SHIB_CONFIG_FAILURE,          "Local attribute configuration failure"
++error_code GSSEAP_SHIB_LISTENER_FAILURE,        "Failed to communicate with local attribute server"
++
++#
++# Extensions
++#
++error_code GSSEAP_BINDINGS_MISMATCH,            "Channel bindings do not match"
++error_code GSSEAP_NO_MECHGLUE_SYMBOL,           "Could not find symbol in mechanism glue"
++error_code GSSEAP_BAD_INVOCATION,               "Bad mechanism invoke OID"
++
++end
+diff --git a/mech_eap/import_name.c b/mech_eap/import_name.c
+new file mode 100644
+index 0000000..8049e01
+--- /dev/null
++++ b/mech_eap/import_name.c
+@@ -0,0 +1,47 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Deserialise a name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_import_name(OM_uint32 *minor,
++                gss_buffer_t import_name_buffer,
++                gss_OID input_name_type,
++                gss_name_t *output_name)
++{
++    return gssEapImportName(minor, import_name_buffer,
++                            input_name_type, GSS_C_NO_OID, output_name);
++}
+diff --git a/mech_eap/import_sec_context.c b/mech_eap/import_sec_context.c
+new file mode 100644
+index 0000000..1533a16
+--- /dev/null
++++ b/mech_eap/import_sec_context.c
+@@ -0,0 +1,374 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Deserialise a context handle.
++ */
++
++#include "gssapiP_eap.h"
++
++#define UPDATE_REMAIN(n)    do {                \
++        p += (n);                               \
++        remain -= (n);                          \
++    } while (0)
++
++#define CHECK_REMAIN(n)     do {                \
++        if (remain < (n)) {                     \
++            *minor = GSSEAP_TOK_TRUNC;          \
++            return GSS_S_DEFECTIVE_TOKEN;       \
++        }                                       \
++    } while (0)
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++static OM_uint32
++gssEapImportPartialContext(OM_uint32 *minor,
++                           unsigned char **pBuf,
++                           size_t *pRemain,
++                           gss_ctx_id_t ctx)
++{
++    OM_uint32 major;
++    unsigned char *p = *pBuf;
++    size_t remain = *pRemain;
++    gss_buffer_desc buf;
++    size_t ctxLength, serverLen;
++
++    /* Length of partial RADIUS context */
++    CHECK_REMAIN(4);
++    ctxLength = load_uint32_be(p);
++    UPDATE_REMAIN(4);
++
++    CHECK_REMAIN(ctxLength);
++    remain = ctxLength; /* check against partial context length */
++
++    /* Selected RADIUS server */
++    CHECK_REMAIN(4);
++    serverLen = load_uint32_be(p);
++    UPDATE_REMAIN(4);
++
++    if (serverLen != 0) {
++        CHECK_REMAIN(serverLen);
++
++        ctx->acceptorCtx.radServer = GSSEAP_MALLOC(serverLen + 1);
++        if (ctx->acceptorCtx.radServer == NULL) {
++            *minor = ENOMEM;
++            return GSS_S_FAILURE;
++        }
++        memcpy(ctx->acceptorCtx.radServer, p, serverLen);
++        ctx->acceptorCtx.radServer[serverLen] = '\0';
++
++        UPDATE_REMAIN(serverLen);
++    }
++
++    /* RADIUS state blob */
++    CHECK_REMAIN(4);
++    buf.length = load_uint32_be(p);
++    UPDATE_REMAIN(4);
++
++    if (buf.length != 0) {
++        CHECK_REMAIN(buf.length);
++
++        buf.value = p;
++
++        major = duplicateBuffer(minor, &buf, &ctx->acceptorCtx.state);
++        if (GSS_ERROR(major))
++            return major;
++
++        UPDATE_REMAIN(buf.length);
++    }
++
++#ifdef GSSEAP_DEBUG
++    GSSEAP_ASSERT(remain == 0);
++#endif
++
++    *pBuf = p;
++    *pRemain -= 4 + ctxLength;
++
++    return GSS_S_COMPLETE;
++}
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++
++static OM_uint32
++importMechanismOid(OM_uint32 *minor,
++                   unsigned char **pBuf,
++                   size_t *pRemain,
++                   gss_OID *pOid)
++{
++    OM_uint32 major;
++    unsigned char *p = *pBuf;
++    size_t remain = *pRemain;
++    gss_OID_desc oidBuf;
++
++    oidBuf.length = load_uint32_be(p);
++    if (remain < 4 + oidBuf.length || oidBuf.length == 0) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    oidBuf.elements = &p[4];
++
++    major = gssEapCanonicalizeOid(minor, &oidBuf, 0, pOid);
++    if (GSS_ERROR(major))
++        return major;
++
++    *pBuf    += 4 + oidBuf.length;
++    *pRemain -= 4 + oidBuf.length;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++importKerberosKey(OM_uint32 *minor,
++                  unsigned char **pBuf,
++                  size_t *pRemain,
++                  krb5_cksumtype *checksumType,
++                  krb5_enctype *pEncryptionType,
++                  krb5_keyblock *pKey)
++{
++    unsigned char *p = *pBuf;
++    size_t remain = *pRemain;
++    OM_uint32 encryptionType;
++    OM_uint32 length;
++    krb5_context krbContext;
++    krb5_keyblock key;
++    krb5_error_code code;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    KRB_KEY_INIT(pKey);
++
++    if (remain < 12) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    *checksumType  = load_uint32_be(&p[0]);
++    encryptionType = load_uint32_be(&p[4]);
++    length         = load_uint32_be(&p[8]);
++
++    if ((length != 0) != (encryptionType != ENCTYPE_NULL)) {
++        *minor = GSSEAP_BAD_CONTEXT_TOKEN;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    if (remain - 12 < length) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    if (encryptionType != ENCTYPE_NULL) {
++        KRB_KEY_INIT(&key);
++
++        KRB_KEY_TYPE(&key)   = encryptionType;
++        KRB_KEY_LENGTH(&key) = length;
++        KRB_KEY_DATA(&key)   = &p[12];
++
++        code = krb5_copy_keyblock_contents(krbContext, &key, pKey);
++        if (code != 0) {
++            *minor = code;
++            return GSS_S_FAILURE;
++        }
++    }
++
++    *pBuf    += 12 + length;
++    *pRemain -= 12 + length;
++    *pEncryptionType = encryptionType;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++importName(OM_uint32 *minor,
++           unsigned char **pBuf,
++           size_t *pRemain,
++           gss_name_t *pName)
++{
++    OM_uint32 major;
++    unsigned char *p = *pBuf;
++    size_t remain = *pRemain;
++    gss_buffer_desc tmp;
++
++    if (remain < 4) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    tmp.length = load_uint32_be(p);
++    if (tmp.length != 0) {
++        if (remain - 4 < tmp.length) {
++            *minor = GSSEAP_TOK_TRUNC;
++            return GSS_S_DEFECTIVE_TOKEN;
++        }
++
++        tmp.value = p + 4;
++
++        major = gssEapImportNameInternal(minor, &tmp, pName,
++                                         EXPORT_NAME_FLAG_COMPOSITE);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    *pBuf    += 4 + tmp.length;
++    *pRemain -= 4 + tmp.length;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapImportContext(OM_uint32 *minor,
++                    gss_buffer_t token,
++                    gss_ctx_id_t ctx)
++{
++    OM_uint32 major;
++    unsigned char *p = (unsigned char *)token->value;
++    size_t remain = token->length;
++
++    if (remain < 16) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++    if (load_uint32_be(&p[0]) != EAP_EXPORT_CONTEXT_V1) {
++        *minor = GSSEAP_BAD_CONTEXT_TOKEN;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++    ctx->state      = load_uint32_be(&p[4]);
++    ctx->flags      = load_uint32_be(&p[8]);
++    ctx->gssFlags   = load_uint32_be(&p[12]);
++    p      += 16;
++    remain -= 16;
++
++    /* Validate state */
++    if (GSSEAP_SM_STATE(ctx) < GSSEAP_STATE_INITIAL ||
++        GSSEAP_SM_STATE(ctx) > GSSEAP_STATE_ESTABLISHED)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    /* Only acceptor can export partial context tokens */
++    if (CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx))
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    major = importMechanismOid(minor, &p, &remain, &ctx->mechanismUsed);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = importKerberosKey(minor, &p, &remain,
++                              &ctx->checksumType,
++                              &ctx->encryptionType,
++                              &ctx->rfc3961Key);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = importName(minor, &p, &remain, &ctx->initiatorName);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = importName(minor, &p, &remain, &ctx->acceptorName);
++    if (GSS_ERROR(major))
++        return major;
++
++    /* Check that, if context is established, names are valid */
++    if (CTX_IS_ESTABLISHED(ctx) &&
++        (CTX_IS_INITIATOR(ctx) ? ctx->acceptorName == GSS_C_NO_NAME
++                               : ctx->initiatorName == GSS_C_NO_NAME)) {
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    if (remain < 24 + sequenceSize(ctx->seqState)) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++    ctx->expiryTime = (time_t)load_uint64_be(&p[0]);
++    ctx->sendSeq    = load_uint64_be(&p[8]);
++    ctx->recvSeq    = load_uint64_be(&p[16]);
++    p      += 24;
++    remain -= 24;
++
++    major = sequenceInternalize(minor, &ctx->seqState, &p, &remain);
++    if (GSS_ERROR(major))
++        return major;
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    /*
++     * The partial context should only be expected for unestablished
++     * acceptor contexts.
++     */
++    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) &&
++        (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
++        major = gssEapImportPartialContext(minor, &p, &remain, ctx);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++#ifdef GSSEAP_DEBUG
++    GSSEAP_ASSERT(remain == 0);
++#endif
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++    return major;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_import_sec_context(OM_uint32 *minor,
++                       gss_buffer_t interprocess_token,
++                       gss_ctx_id_t *context_handle)
++{
++    OM_uint32 major, tmpMinor;
++    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
++
++    *context_handle = GSS_C_NO_CONTEXT;
++
++    if (interprocess_token == GSS_C_NO_BUFFER ||
++        interprocess_token->length == 0) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    major = gssEapAllocContext(minor, &ctx);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapImportContext(minor, interprocess_token, ctx);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    *context_handle = ctx;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gssEapReleaseContext(&tmpMinor, &ctx);
++
++    return major;
++}
+diff --git a/mech_eap/indicate_mechs.c b/mech_eap/indicate_mechs.c
+new file mode 100644
+index 0000000..d4d275e
+--- /dev/null
++++ b/mech_eap/indicate_mechs.c
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Enumerate the supported mechanism OIDs.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_indicate_mechs(OM_uint32 *minor,
++                   gss_OID_set *mech_set)
++{
++    return gssEapIndicateMechs(minor, mech_set);
++}
+diff --git a/mech_eap/init_sec_context.c b/mech_eap/init_sec_context.c
+new file mode 100644
+index 0000000..e99b479
+--- /dev/null
++++ b/mech_eap/init_sec_context.c
+@@ -0,0 +1,1097 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Establish a security context on the initiator (client). These functions
++ * wrap around libeap.
++ */
++
++#include "gssapiP_eap.h"
++
++static OM_uint32
++policyVariableToFlag(enum eapol_bool_var variable)
++{
++    OM_uint32 flag = 0;
++
++    switch (variable) {
++    case EAPOL_eapSuccess:
++        flag = CTX_FLAG_EAP_SUCCESS;
++        break;
++    case EAPOL_eapRestart:
++        flag = CTX_FLAG_EAP_RESTART;
++        break;
++    case EAPOL_eapFail:
++        flag = CTX_FLAG_EAP_FAIL;
++        break;
++    case EAPOL_eapResp:
++        flag = CTX_FLAG_EAP_RESP;
++        break;
++    case EAPOL_eapNoResp:
++        flag = CTX_FLAG_EAP_NO_RESP;
++        break;
++    case EAPOL_eapReq:
++        flag = CTX_FLAG_EAP_REQ;
++        break;
++    case EAPOL_portEnabled:
++        flag = CTX_FLAG_EAP_PORT_ENABLED;
++        break;
++    case EAPOL_altAccept:
++        flag = CTX_FLAG_EAP_ALT_ACCEPT;
++        break;
++    case EAPOL_altReject:
++        flag = CTX_FLAG_EAP_ALT_REJECT;
++        break;
++    }
++
++    return flag;
++}
++
++static struct eap_peer_config *
++peerGetConfig(void *ctx)
++{
++    gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
++
++    return &gssCtx->initiatorCtx.eapPeerConfig;
++}
++
++static Boolean
++peerGetBool(void *data, enum eapol_bool_var variable)
++{
++    gss_ctx_id_t ctx = data;
++    OM_uint32 flag;
++
++    if (ctx == GSS_C_NO_CONTEXT)
++        return FALSE;
++
++    flag = policyVariableToFlag(variable);
++
++    return ((ctx->flags & flag) != 0);
++}
++
++static void
++peerSetBool(void *data, enum eapol_bool_var variable,
++            Boolean value)
++{
++    gss_ctx_id_t ctx = data;
++    OM_uint32 flag;
++
++    if (ctx == GSS_C_NO_CONTEXT)
++        return;
++
++    flag = policyVariableToFlag(variable);
++
++    if (value)
++        ctx->flags |= flag;
++    else
++        ctx->flags &= ~(flag);
++}
++
++static unsigned int
++peerGetInt(void *data, enum eapol_int_var variable)
++{
++    gss_ctx_id_t ctx = data;
++
++    if (ctx == GSS_C_NO_CONTEXT)
++        return FALSE;
++
++    GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
++
++    switch (variable) {
++    case EAPOL_idleWhile:
++        return ctx->initiatorCtx.idleWhile;
++        break;
++    }
++
++    return 0;
++}
++
++static void
++peerSetInt(void *data, enum eapol_int_var variable,
++           unsigned int value)
++{
++    gss_ctx_id_t ctx = data;
++
++    if (ctx == GSS_C_NO_CONTEXT)
++        return;
++
++    GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));
++
++    switch (variable) {
++    case EAPOL_idleWhile:
++        ctx->initiatorCtx.idleWhile = value;
++        break;
++    }
++}
++
++static struct wpabuf *
++peerGetEapReqData(void *ctx)
++{
++    gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
++
++    return &gssCtx->initiatorCtx.reqData;
++}
++
++static void
++peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
++                  struct wpa_config_blob *blob GSSEAP_UNUSED)
++{
++}
++
++static const struct wpa_config_blob *
++peerGetConfigBlob(void *ctx GSSEAP_UNUSED,
++                  const char *name GSSEAP_UNUSED)
++{
++    return NULL;
++}
++
++static void
++peerNotifyPending(void *ctx GSSEAP_UNUSED)
++{
++}
++
++static struct eapol_callbacks gssEapPolicyCallbacks = {
++    peerGetConfig,
++    peerGetBool,
++    peerSetBool,
++    peerGetInt,
++    peerSetInt,
++    peerGetEapReqData,
++    peerSetConfigBlob,
++    peerGetConfigBlob,
++    peerNotifyPending,
++};
++
++#ifdef GSSEAP_DEBUG
++extern int wpa_debug_level;
++#endif
++
++static OM_uint32
++peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
++{
++    OM_uint32 major;
++    krb5_context krbContext;
++    struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
++    gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
++    gss_cred_id_t cred = ctx->cred;
++
++    eapPeerConfig->identity = NULL;
++    eapPeerConfig->identity_len = 0;
++    eapPeerConfig->anonymous_identity = NULL;
++    eapPeerConfig->anonymous_identity_len = 0;
++    eapPeerConfig->password = NULL;
++    eapPeerConfig->password_len = 0;
++
++    GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    eapPeerConfig->fragment_size = 1024;
++#ifdef GSSEAP_DEBUG
++    wpa_debug_level = 0;
++#endif
++
++    GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
++
++    if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
++        *minor = GSSEAP_BAD_INITIATOR_NAME;
++        return GSS_S_BAD_NAME;
++    }
++
++    /* identity */
++    major = gssEapDisplayName(minor, cred->name, &identity, NULL);
++    if (GSS_ERROR(major))
++        return major;
++
++    eapPeerConfig->identity = (unsigned char *)identity.value;
++    eapPeerConfig->identity_len = identity.length;
++
++    krbPrincRealmToGssBuffer(cred->name->krbPrincipal, &realm);
++
++    /* anonymous_identity */
++    eapPeerConfig->anonymous_identity = GSSEAP_MALLOC(realm.length + 2);
++    if (eapPeerConfig->anonymous_identity == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    eapPeerConfig->anonymous_identity[0] = '@';
++    memcpy(eapPeerConfig->anonymous_identity + 1, realm.value, realm.length);
++    eapPeerConfig->anonymous_identity[1 + realm.length] = '\0';
++    eapPeerConfig->anonymous_identity_len = 1 + realm.length;
++
++    /* password */
++    eapPeerConfig->password = (unsigned char *)cred->password.value;
++    eapPeerConfig->password_len = cred->password.length;
++
++    /* certs */
++    eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
++    eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
++    eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++peerConfigFree(OM_uint32 *minor,
++               gss_ctx_id_t ctx)
++{
++    struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
++
++    if (eapPeerConfig->identity != NULL) {
++        GSSEAP_FREE(eapPeerConfig->identity);
++        eapPeerConfig->identity = NULL;
++        eapPeerConfig->identity_len = 0;
++    }
++
++    if (eapPeerConfig->anonymous_identity != NULL) {
++        GSSEAP_FREE(eapPeerConfig->anonymous_identity);
++        eapPeerConfig->anonymous_identity = NULL;
++        eapPeerConfig->anonymous_identity_len = 0;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/*
++ * Mark an initiator context as ready for cryptographic operations
++ */
++static OM_uint32
++initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
++{
++    OM_uint32 major;
++    const unsigned char *key;
++    size_t keyLength;
++
++#if 1
++    /* XXX actually check for mutual auth */
++    if (reqFlags & GSS_C_MUTUAL_FLAG)
++        ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
++#endif
++
++    /* Cache encryption type derived from selected mechanism OID */
++    major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (!eap_key_available(ctx->initiatorCtx.eap)) {
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    key = eap_get_eapKeyData(ctx->initiatorCtx.eap, &keyLength);
++
++    if (keyLength < EAP_EMSK_LEN) {
++        *minor = GSSEAP_KEY_TOO_SHORT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    major = gssEapDeriveRfc3961Key(minor,
++                                   &key[EAP_EMSK_LEN / 2],
++                                   EAP_EMSK_LEN / 2,
++                                   ctx->encryptionType,
++                                   &ctx->rfc3961Key);
++       if (GSS_ERROR(major))
++           return major;
++
++    major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
++                                      &ctx->checksumType);
++    if (GSS_ERROR(major))
++        return major;
++
++    major = sequenceInit(minor,
++                         &ctx->seqState,
++                         ctx->recvSeq,
++                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
++                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
++                         TRUE);
++    if (GSS_ERROR(major))
++        return major;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++initBegin(OM_uint32 *minor,
++          gss_ctx_id_t ctx,
++          gss_name_t target,
++          gss_OID mech,
++          OM_uint32 reqFlags GSSEAP_UNUSED,
++          OM_uint32 timeReq,
++          gss_channel_bindings_t chanBindings GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++    gss_cred_id_t cred = ctx->cred;
++
++    GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
++
++    if (cred->expiryTime)
++        ctx->expiryTime = cred->expiryTime;
++    else if (timeReq == 0 || timeReq == GSS_C_INDEFINITE)
++        ctx->expiryTime = 0;
++    else
++        ctx->expiryTime = time(NULL) + timeReq;
++
++    /*
++     * The credential mutex protects its name, however we need to
++     * explicitly lock the acceptor name (unlikely as it may be
++     * that it has attributes set on it).
++     */
++    major = gssEapDuplicateName(minor, cred->name, &ctx->initiatorName);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (target != GSS_C_NO_NAME) {
++        GSSEAP_MUTEX_LOCK(&target->mutex);
++
++        major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
++        if (GSS_ERROR(major)) {
++            GSSEAP_MUTEX_UNLOCK(&target->mutex);
++            return major;
++        }
++
++        GSSEAP_MUTEX_UNLOCK(&target->mutex);
++    }
++
++    major = gssEapCanonicalizeOid(minor,
++                                  mech,
++                                  OID_FLAG_NULL_VALID | OID_FLAG_MAP_NULL_TO_DEFAULT_MECH,
++                                  &ctx->mechanismUsed);
++    if (GSS_ERROR(major))
++        return major;
++
++    /* If credentials were provided, check they're usable with this mech */
++    if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
++        *minor = GSSEAP_CRED_MECH_MISMATCH;
++        return GSS_S_BAD_MECH;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++eapGssSmInitError(OM_uint32 *minor,
++                  gss_cred_id_t cred GSSEAP_UNUSED,
++                  gss_ctx_id_t ctx GSSEAP_UNUSED,
++                  gss_name_t target GSSEAP_UNUSED,
++                  gss_OID mech GSSEAP_UNUSED,
++                  OM_uint32 reqFlags GSSEAP_UNUSED,
++                  OM_uint32 timeReq GSSEAP_UNUSED,
++                  gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                  gss_buffer_t inputToken,
++                  gss_buffer_t outputToken GSSEAP_UNUSED,
++                  OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++    unsigned char *p;
++
++    if (inputToken->length < 8) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    p = (unsigned char *)inputToken->value;
++
++    major = load_uint32_be(&p[0]);
++    *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
++
++    if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
++        major = GSS_S_FAILURE;
++        *minor = GSSEAP_BAD_ERROR_TOKEN;
++    }
++
++    GSSEAP_ASSERT(GSS_ERROR(major));
++
++    return major;
++}
++
++#ifdef GSSEAP_ENABLE_REAUTH
++static OM_uint32
++eapGssSmInitGssReauth(OM_uint32 *minor,
++                      gss_cred_id_t cred,
++                      gss_ctx_id_t ctx,
++                      gss_name_t target,
++                      gss_OID mech GSSEAP_UNUSED,
++                      OM_uint32 reqFlags,
++                      OM_uint32 timeReq,
++                      gss_channel_bindings_t chanBindings,
++                      gss_buffer_t inputToken,
++                      gss_buffer_t outputToken,
++                      OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major, tmpMinor;
++    gss_name_t mechTarget = GSS_C_NO_NAME;
++    gss_OID actualMech = GSS_C_NO_OID;
++    OM_uint32 gssFlags, timeRec;
++
++    /*
++     * Here we use the passed in credential handle because the resolved
++     * context credential does not currently have the reauth creds.
++     */
++    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
++        if (!gssEapCanReauthP(cred, target, timeReq))
++            return GSS_S_CONTINUE_NEEDED;
++
++        ctx->flags |= CTX_FLAG_KRB_REAUTH;
++    } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
++        major = GSS_S_DEFECTIVE_TOKEN;
++        *minor = GSSEAP_WRONG_ITOK;
++        goto cleanup;
++    }
++
++    GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
++
++    major = gssEapMechToGlueName(minor, target, &mechTarget);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssInitSecContext(minor,
++                              cred->reauthCred,
++                              &ctx->reauthCtx,
++                              mechTarget,
++                              (gss_OID)gss_mech_krb5,
++                              reqFlags | GSS_C_MUTUAL_FLAG,
++                              timeReq,
++                              chanBindings,
++                              inputToken,
++                              &actualMech,
++                              outputToken,
++                              &gssFlags,
++                              &timeRec);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    ctx->gssFlags = gssFlags;
++
++    if (major == GSS_S_COMPLETE) {
++        GSSEAP_ASSERT(GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE);
++
++        major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
++        if (GSS_ERROR(major))
++            goto cleanup;
++        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
++    } else {
++        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
++    }
++
++cleanup:
++    gssReleaseName(&tmpMinor, &mechTarget);
++
++    return major;
++}
++#endif /* GSSEAP_ENABLE_REAUTH */
++
++#ifdef GSSEAP_DEBUG
++static OM_uint32
++eapGssSmInitVendorInfo(OM_uint32 *minor,
++                       gss_cred_id_t cred GSSEAP_UNUSED,
++                       gss_ctx_id_t ctx GSSEAP_UNUSED,
++                       gss_name_t target GSSEAP_UNUSED,
++                       gss_OID mech GSSEAP_UNUSED,
++                       OM_uint32 reqFlags GSSEAP_UNUSED,
++                       OM_uint32 timeReq GSSEAP_UNUSED,
++                       gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                       gss_buffer_t inputToken GSSEAP_UNUSED,
++                       gss_buffer_t outputToken,
++                       OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    major = makeStringBuffer(minor, "JANET(UK)", outputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++#endif
++
++static OM_uint32
++eapGssSmInitAcceptorName(OM_uint32 *minor,
++                         gss_cred_id_t cred GSSEAP_UNUSED,
++                         gss_ctx_id_t ctx,
++                         gss_name_t target GSSEAP_UNUSED,
++                         gss_OID mech GSSEAP_UNUSED,
++                         OM_uint32 reqFlags GSSEAP_UNUSED,
++                         OM_uint32 timeReq GSSEAP_UNUSED,
++                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                         gss_buffer_t inputToken GSSEAP_UNUSED,
++                         gss_buffer_t outputToken,
++                         OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL &&
++        ctx->acceptorName != GSS_C_NO_NAME) {
++
++        /* Send desired target name to acceptor */
++        major = gssEapDisplayName(minor, ctx->acceptorName,
++                                  outputToken, NULL);
++        if (GSS_ERROR(major))
++            return major;
++    } else if (inputToken != GSS_C_NO_BUFFER &&
++               ctx->acceptorName == GSS_C_NO_NAME) {
++        /* Accept target name hint from acceptor */
++        major = gssEapImportName(minor, inputToken,
++                                 GSS_C_NT_USER_NAME,
++                                 ctx->mechanismUsed,
++                                 &ctx->acceptorName);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    /*
++     * Currently, other parts of the code assume that the acceptor name
++     * is available, hence this check.
++     */
++    if (ctx->acceptorName == GSS_C_NO_NAME) {
++        *minor = GSSEAP_NO_ACCEPTOR_NAME;
++        return GSS_S_FAILURE;
++    }
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++static OM_uint32
++eapGssSmInitIdentity(OM_uint32 *minor,
++                     gss_cred_id_t cred GSSEAP_UNUSED,
++                     gss_ctx_id_t ctx,
++                     gss_name_t target GSSEAP_UNUSED,
++                     gss_OID mech GSSEAP_UNUSED,
++                     OM_uint32 reqFlags GSSEAP_UNUSED,
++                     OM_uint32 timeReq GSSEAP_UNUSED,
++                     gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                     gss_buffer_t inputToken GSSEAP_UNUSED,
++                     gss_buffer_t outputToken GSSEAP_UNUSED,
++                     OM_uint32 *smFlags)
++{
++    struct eap_config eapConfig;
++
++#ifdef GSSEAP_ENABLE_REAUTH
++    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
++        OM_uint32 tmpMinor;
++
++        /* server didn't support reauthentication, sent EAP request */
++        gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
++        ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
++        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
++    } else
++#endif
++        *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
++
++    GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
++    GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
++
++    memset(&eapConfig, 0, sizeof(eapConfig));
++
++    ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
++                                             &gssEapPolicyCallbacks,
++                                             ctx,
++                                             &eapConfig);
++    if (ctx->initiatorCtx.eap == NULL) {
++        *minor = GSSEAP_PEER_SM_INIT_FAILURE;
++        return GSS_S_FAILURE;
++    }
++
++    ctx->flags |= CTX_FLAG_EAP_RESTART | CTX_FLAG_EAP_PORT_ENABLED;
++
++    /* poke EAP state machine */
++    if (eap_peer_sm_step(ctx->initiatorCtx.eap) != 0) {
++        *minor = GSSEAP_PEER_SM_STEP_FAILURE;
++        return GSS_S_FAILURE;
++    }
++
++    GSSEAP_SM_TRANSITION_NEXT(ctx);
++
++    *minor = 0;
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++static OM_uint32
++eapGssSmInitAuthenticate(OM_uint32 *minor,
++                         gss_cred_id_t cred GSSEAP_UNUSED,
++                         gss_ctx_id_t ctx,
++                         gss_name_t target GSSEAP_UNUSED,
++                         gss_OID mech GSSEAP_UNUSED,
++                         OM_uint32 reqFlags GSSEAP_UNUSED,
++                         OM_uint32 timeReq GSSEAP_UNUSED,
++                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                         gss_buffer_t inputToken GSSEAP_UNUSED,
++                         gss_buffer_t outputToken,
++                         OM_uint32 *smFlags)
++{
++    OM_uint32 major;
++    OM_uint32 tmpMinor;
++    struct wpabuf *resp = NULL;
++
++    *minor = 0;
++
++    GSSEAP_ASSERT(inputToken != GSS_C_NO_BUFFER);
++
++    major = peerConfigInit(minor, ctx);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    GSSEAP_ASSERT(ctx->initiatorCtx.eap != NULL);
++    GSSEAP_ASSERT(ctx->flags & CTX_FLAG_EAP_PORT_ENABLED);
++
++    ctx->flags |= CTX_FLAG_EAP_REQ; /* we have a Request from the acceptor */
++
++    wpabuf_set(&ctx->initiatorCtx.reqData,
++               inputToken->value, inputToken->length);
++
++    major = GSS_S_CONTINUE_NEEDED;
++
++    eap_peer_sm_step(ctx->initiatorCtx.eap);
++    if (ctx->flags & CTX_FLAG_EAP_RESP) {
++        ctx->flags &= ~(CTX_FLAG_EAP_RESP);
++
++        resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
++    } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
++        major = initReady(minor, ctx, reqFlags);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
++        major = GSS_S_CONTINUE_NEEDED;
++        GSSEAP_SM_TRANSITION_NEXT(ctx);
++    } else if (ctx->flags & CTX_FLAG_EAP_FAIL) {
++        major = GSS_S_DEFECTIVE_CREDENTIAL;
++        *minor = GSSEAP_PEER_AUTH_FAILURE;
++    } else {
++        major = GSS_S_DEFECTIVE_TOKEN;
++        *minor = GSSEAP_PEER_BAD_MESSAGE;
++    }
++
++cleanup:
++    if (resp != NULL) {
++        OM_uint32 tmpMajor;
++        gss_buffer_desc respBuf;
++
++        GSSEAP_ASSERT(major == GSS_S_CONTINUE_NEEDED);
++
++        respBuf.length = wpabuf_len(resp);
++        respBuf.value = (void *)wpabuf_head(resp);
++
++        tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
++        if (GSS_ERROR(tmpMajor)) {
++            major = tmpMajor;
++            *minor = tmpMinor;
++        }
++
++        *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
++    }
++
++    wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
++    peerConfigFree(&tmpMinor, ctx);
++
++    return major;
++}
++
++static OM_uint32
++eapGssSmInitGssFlags(OM_uint32 *minor,
++                     gss_cred_id_t cred GSSEAP_UNUSED,
++                     gss_ctx_id_t ctx,
++                     gss_name_t target GSSEAP_UNUSED,
++                     gss_OID mech GSSEAP_UNUSED,
++                     OM_uint32 reqFlags GSSEAP_UNUSED,
++                     OM_uint32 timeReq GSSEAP_UNUSED,
++                     gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                     gss_buffer_t inputToken GSSEAP_UNUSED,
++                     gss_buffer_t outputToken,
++                     OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    unsigned char wireFlags[4];
++    gss_buffer_desc flagsBuf;
++
++    store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
++
++    flagsBuf.length = sizeof(wireFlags);
++    flagsBuf.value = wireFlags;
++
++    return duplicateBuffer(minor, &flagsBuf, outputToken);
++}
++
++static OM_uint32
++eapGssSmInitGssChannelBindings(OM_uint32 *minor,
++                               gss_cred_id_t cred GSSEAP_UNUSED,
++                               gss_ctx_id_t ctx,
++                               gss_name_t target GSSEAP_UNUSED,
++                               gss_OID mech GSSEAP_UNUSED,
++                               OM_uint32 reqFlags GSSEAP_UNUSED,
++                               OM_uint32 timeReq GSSEAP_UNUSED,
++                               gss_channel_bindings_t chanBindings,
++                               gss_buffer_t inputToken GSSEAP_UNUSED,
++                               gss_buffer_t outputToken,
++                               OM_uint32 *smFlags)
++{
++    OM_uint32 major;
++    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
++
++    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
++        buffer = chanBindings->application_data;
++
++    major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
++                       &buffer, NULL, outputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    GSSEAP_ASSERT(outputToken->value != NULL);
++
++    *minor = 0;
++    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++static OM_uint32
++eapGssSmInitInitiatorMIC(OM_uint32 *minor,
++                         gss_cred_id_t cred GSSEAP_UNUSED,
++                         gss_ctx_id_t ctx,
++                         gss_name_t target GSSEAP_UNUSED,
++                         gss_OID mech GSSEAP_UNUSED,
++                         OM_uint32 reqFlags GSSEAP_UNUSED,
++                         OM_uint32 timeReq GSSEAP_UNUSED,
++                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                         gss_buffer_t inputToken GSSEAP_UNUSED,
++                         gss_buffer_t outputToken,
++                         OM_uint32 *smFlags)
++{
++    OM_uint32 major;
++
++    major = gssEapMakeTokenMIC(minor, ctx, outputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    GSSEAP_SM_TRANSITION_NEXT(ctx);
++
++    *minor = 0;
++    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
++
++    return GSS_S_CONTINUE_NEEDED;
++}
++ 
++#ifdef GSSEAP_ENABLE_REAUTH
++static OM_uint32
++eapGssSmInitReauthCreds(OM_uint32 *minor,
++                        gss_cred_id_t cred,
++                        gss_ctx_id_t ctx,
++                        gss_name_t target GSSEAP_UNUSED,
++                        gss_OID mech GSSEAP_UNUSED,
++                        OM_uint32 reqFlags GSSEAP_UNUSED,
++                        OM_uint32 timeReq GSSEAP_UNUSED,
++                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                        gss_buffer_t inputToken,
++                        gss_buffer_t outputToken GSSEAP_UNUSED,
++                        OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    if (ctx->gssFlags & GSS_C_MUTUAL_FLAG) {
++        major = gssEapStoreReauthCreds(minor, ctx, cred, inputToken);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    *minor = 0;
++    return GSS_S_CONTINUE_NEEDED;
++}
++#endif /* GSSEAP_ENABLE_REAUTH */
++
++static OM_uint32
++eapGssSmInitAcceptorMIC(OM_uint32 *minor,
++                        gss_cred_id_t cred GSSEAP_UNUSED,
++                        gss_ctx_id_t ctx,
++                        gss_name_t target GSSEAP_UNUSED,
++                        gss_OID mech GSSEAP_UNUSED,
++                        OM_uint32 reqFlags GSSEAP_UNUSED,
++                        OM_uint32 timeReq GSSEAP_UNUSED,
++                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
++                        gss_buffer_t inputToken,
++                        gss_buffer_t outputToken GSSEAP_UNUSED,
++                        OM_uint32 *smFlags GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++
++    major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
++
++    *minor = 0;
++
++    return GSS_S_COMPLETE;
++}
++
++static struct gss_eap_sm eapGssInitiatorSm[] = {
++    {
++        ITOK_TYPE_CONTEXT_ERR,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_ALL & ~(GSSEAP_STATE_INITIAL),
++        0,
++        eapGssSmInitError
++    },
++    {
++        ITOK_TYPE_ACCEPTOR_NAME_RESP,
++        ITOK_TYPE_ACCEPTOR_NAME_REQ,
++        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
++        0,
++        eapGssSmInitAcceptorName
++    },
++#ifdef GSSEAP_DEBUG
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_VENDOR_INFO,
++        GSSEAP_STATE_INITIAL,
++        0,
++        eapGssSmInitVendorInfo
++    },
++#endif
++#ifdef GSSEAP_ENABLE_REAUTH
++    {
++        ITOK_TYPE_REAUTH_RESP,
++        ITOK_TYPE_REAUTH_REQ,
++        GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
++        0,
++        eapGssSmInitGssReauth
++    },
++#endif
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_NONE,
++#ifdef GSSEAP_ENABLE_REAUTH
++        GSSEAP_STATE_REAUTHENTICATE |
++#endif
++        GSSEAP_STATE_INITIAL,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmInitIdentity
++    },
++    {
++        ITOK_TYPE_EAP_REQ,
++        ITOK_TYPE_EAP_RESP,
++        GSSEAP_STATE_AUTHENTICATE,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmInitAuthenticate
++    },
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_GSS_FLAGS,
++        GSSEAP_STATE_INITIATOR_EXTS,
++        0,
++        eapGssSmInitGssFlags
++    },
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_GSS_CHANNEL_BINDINGS,
++        GSSEAP_STATE_INITIATOR_EXTS,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmInitGssChannelBindings
++    },
++    {
++        ITOK_TYPE_NONE,
++        ITOK_TYPE_INITIATOR_MIC,
++        GSSEAP_STATE_INITIATOR_EXTS,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmInitInitiatorMIC
++    },
++#ifdef GSSEAP_ENABLE_REAUTH
++    {
++        ITOK_TYPE_REAUTH_CREDS,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_ACCEPTOR_EXTS,
++        0,
++        eapGssSmInitReauthCreds
++    },
++#endif
++    /* other extensions go here */
++    {
++        ITOK_TYPE_ACCEPTOR_MIC,
++        ITOK_TYPE_NONE,
++        GSSEAP_STATE_ACCEPTOR_EXTS,
++        SM_ITOK_FLAG_REQUIRED,
++        eapGssSmInitAcceptorMIC
++    }
++};
++
++OM_uint32
++gssEapInitSecContext(OM_uint32 *minor,
++                     gss_cred_id_t cred,
++                     gss_ctx_id_t ctx,
++                     gss_name_t target_name,
++                     gss_OID mech_type,
++                     OM_uint32 req_flags,
++                     OM_uint32 time_req,
++                     gss_channel_bindings_t input_chan_bindings,
++                     gss_buffer_t input_token,
++                     gss_OID *actual_mech_type,
++                     gss_buffer_t output_token,
++                     OM_uint32 *ret_flags,
++                     OM_uint32 *time_rec)
++{
++    OM_uint32 major, tmpMinor;
++    int initialContextToken = (ctx->mechanismUsed == GSS_C_NO_OID);
++
++    /*
++     * XXX is acquiring the credential lock here necessary? The password is
++     * mutable but the contract could specify that this is not updated whilst
++     * a context is being initialized.
++     */
++    if (cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    if (ctx->cred == GSS_C_NO_CREDENTIAL) {
++        major = gssEapResolveInitiatorCred(minor, cred, target_name, &ctx->cred);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        GSSEAP_ASSERT(ctx->cred != GSS_C_NO_CREDENTIAL);
++    }
++
++    GSSEAP_MUTEX_LOCK(&ctx->cred->mutex);
++
++    GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_RESOLVED);
++    GSSEAP_ASSERT(ctx->cred->flags & CRED_FLAG_INITIATE);
++
++    if (initialContextToken) {
++        major = initBegin(minor, ctx, target_name, mech_type,
++                          req_flags, time_req, input_chan_bindings);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    major = gssEapSmStep(minor,
++                         cred,
++                         ctx,
++                         target_name,
++                         mech_type,
++                         req_flags,
++                         time_req,
++                         input_chan_bindings,
++                         input_token,
++                         output_token,
++                         eapGssInitiatorSm,
++                         sizeof(eapGssInitiatorSm) / sizeof(eapGssInitiatorSm[0]));
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (actual_mech_type != NULL) {
++        OM_uint32 tmpMajor;
++
++        tmpMajor = gssEapCanonicalizeOid(&tmpMinor, ctx->mechanismUsed, 0, actual_mech_type);
++        if (GSS_ERROR(tmpMajor)) {
++            major = tmpMajor;
++            *minor = tmpMinor;
++            goto cleanup;
++        }
++    }
++    if (ret_flags != NULL)
++        *ret_flags = ctx->gssFlags;
++    if (time_rec != NULL)
++        gssEapContextTime(&tmpMinor, ctx, time_rec);
++
++    GSSEAP_ASSERT(CTX_IS_ESTABLISHED(ctx) || major == GSS_S_CONTINUE_NEEDED);
++
++cleanup:
++    if (cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++    if (ctx->cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_UNLOCK(&ctx->cred->mutex);
++
++    return major;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_init_sec_context(OM_uint32 *minor,
++                     gss_cred_id_t cred,
++                     gss_ctx_id_t *context_handle,
++                     gss_name_t target_name,
++                     gss_OID mech_type,
++                     OM_uint32 req_flags,
++                     OM_uint32 time_req,
++                     gss_channel_bindings_t input_chan_bindings,
++                     gss_buffer_t input_token,
++                     gss_OID *actual_mech_type,
++                     gss_buffer_t output_token,
++                     OM_uint32 *ret_flags,
++                     OM_uint32 *time_rec)
++{
++    OM_uint32 major, tmpMinor;
++    gss_ctx_id_t ctx = *context_handle;
++
++    *minor = 0;
++
++    output_token->length = 0;
++    output_token->value = NULL;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
++            *minor = GSSEAP_WRONG_SIZE;
++            return GSS_S_DEFECTIVE_TOKEN;
++        }
++
++        major = gssEapAllocContext(minor, &ctx);
++        if (GSS_ERROR(major))
++            return major;
++
++        ctx->flags |= CTX_FLAG_INITIATOR;
++
++        *context_handle = ctx;
++    }
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    major = gssEapInitSecContext(minor,
++                                 cred,
++                                 ctx,
++                                 target_name,
++                                 mech_type,
++                                 req_flags,
++                                 time_req,
++                                 input_chan_bindings,
++                                 input_token,
++                                 actual_mech_type,
++                                 output_token,
++                                 ret_flags,
++                                 time_rec);
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    if (GSS_ERROR(major))
++        gssEapReleaseContext(&tmpMinor, context_handle);
++
++    return major;
++}
+diff --git a/mech_eap/inquire_attrs_for_mech.c b/mech_eap/inquire_attrs_for_mech.c
+new file mode 100644
+index 0000000..a359f68
+--- /dev/null
++++ b/mech_eap/inquire_attrs_for_mech.c
+@@ -0,0 +1,137 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Enumerate the features supported by the GSS EAP mechanism.
++ */
++
++#include "gssapiP_eap.h"
++
++#define MA_ADD(ma, set)    do { \
++    major = gss_add_oid_set_member(minor, (gss_OID)(ma), (set));            \
++    if (GSS_ERROR(major))                                                   \
++        goto cleanup;                                                       \
++    } while (0)
++
++#define MA_SUPPORTED(ma)    MA_ADD((ma), mech_attrs)
++#define MA_KNOWN(ma)        MA_ADD((ma), known_mech_attrs)
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_attrs_for_mech(OM_uint32 *minor,
++                           gss_const_OID mech_oid,
++                           gss_OID_set *mech_attrs,
++                           gss_OID_set *known_mech_attrs)
++{
++    OM_uint32 major, tmpMinor;
++
++    if (mech_attrs != NULL)
++        *mech_attrs = GSS_C_NO_OID_SET;
++    if (known_mech_attrs != NULL)
++        *known_mech_attrs = GSS_C_NO_OID_SET;
++
++    if (!gssEapIsConcreteMechanismOid((const gss_OID)mech_oid)) {
++        *minor = GSSEAP_WRONG_MECH;
++        return GSS_S_BAD_MECH;
++    }
++
++    if (mech_attrs != NULL) {
++        major = gss_create_empty_oid_set(minor, mech_attrs);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++#ifdef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH
++        if (oidEqual(mech_oid, GSS_EAP_MECHANISM))
++            MA_SUPPORTED(GSS_C_MA_MECH_PSEUDO);
++        else
++            MA_SUPPORTED(GSS_C_MA_MECH_CONCRETE);
++        MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
++        MA_SUPPORTED(GSS_C_MA_AUTH_INIT);
++        MA_SUPPORTED(GSS_C_MA_AUTH_TARG);
++        MA_SUPPORTED(GSS_C_MA_AUTH_INIT_INIT);
++        MA_SUPPORTED(GSS_C_MA_INTEG_PROT);
++        MA_SUPPORTED(GSS_C_MA_CONF_PROT);
++        MA_SUPPORTED(GSS_C_MA_MIC);
++        MA_SUPPORTED(GSS_C_MA_WRAP);
++        MA_SUPPORTED(GSS_C_MA_REPLAY_DET);
++        MA_SUPPORTED(GSS_C_MA_OOS_DET);
++        MA_SUPPORTED(GSS_C_MA_CBINDINGS);
++        MA_SUPPORTED(GSS_C_MA_CTX_TRANS);
++#endif
++    }
++
++    if (known_mech_attrs != NULL) {
++        major = gss_create_empty_oid_set(minor, known_mech_attrs);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++#ifdef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH
++        MA_KNOWN(GSS_C_MA_MECH_CONCRETE);
++        MA_KNOWN(GSS_C_MA_MECH_PSEUDO);
++        MA_KNOWN(GSS_C_MA_MECH_COMPOSITE);
++        MA_KNOWN(GSS_C_MA_MECH_NEGO);
++        MA_KNOWN(GSS_C_MA_MECH_GLUE);
++        MA_KNOWN(GSS_C_MA_NOT_MECH);
++        MA_KNOWN(GSS_C_MA_DEPRECATED);
++        MA_KNOWN(GSS_C_MA_NOT_DFLT_MECH);
++        MA_KNOWN(GSS_C_MA_ITOK_FRAMED);
++        MA_KNOWN(GSS_C_MA_AUTH_INIT);
++        MA_KNOWN(GSS_C_MA_AUTH_TARG);
++        MA_KNOWN(GSS_C_MA_AUTH_INIT_INIT);
++        MA_KNOWN(GSS_C_MA_AUTH_TARG_INIT);
++        MA_KNOWN(GSS_C_MA_AUTH_INIT_ANON);
++        MA_KNOWN(GSS_C_MA_AUTH_TARG_ANON);
++        MA_KNOWN(GSS_C_MA_DELEG_CRED);
++        MA_KNOWN(GSS_C_MA_INTEG_PROT);
++        MA_KNOWN(GSS_C_MA_CONF_PROT);
++        MA_KNOWN(GSS_C_MA_MIC);
++        MA_KNOWN(GSS_C_MA_WRAP);
++        MA_KNOWN(GSS_C_MA_PROT_READY);
++        MA_KNOWN(GSS_C_MA_REPLAY_DET);
++        MA_KNOWN(GSS_C_MA_OOS_DET);
++        MA_KNOWN(GSS_C_MA_CBINDINGS);
++        MA_KNOWN(GSS_C_MA_PFS);
++        MA_KNOWN(GSS_C_MA_COMPRESS);
++        MA_KNOWN(GSS_C_MA_CTX_TRANS);
++#endif
++    }
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        gss_release_oid_set(&tmpMinor, mech_attrs);
++        gss_release_oid_set(&tmpMinor, known_mech_attrs);
++    }
++
++    return major;
++}
+diff --git a/mech_eap/inquire_context.c b/mech_eap/inquire_context.c
+new file mode 100644
+index 0000000..d37818d
+--- /dev/null
++++ b/mech_eap/inquire_context.c
+@@ -0,0 +1,116 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Return context handle properties.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_context(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    gss_name_t *src_name,
++                    gss_name_t *targ_name,
++                    OM_uint32 *lifetime_rec,
++                    gss_OID *mech_type,
++                    OM_uint32 *ctx_flags,
++                    int *locally_initiated,
++                    int *open)
++{
++    OM_uint32 major, tmpMinor;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (src_name != NULL) {
++        major = gssEapDuplicateName(minor, ctx->initiatorName, src_name);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (targ_name != NULL) {
++        major = gssEapDuplicateName(minor, ctx->acceptorName, targ_name);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (lifetime_rec != NULL) {
++        time_t now, lifetime;
++
++        if (ctx->expiryTime == 0) {
++            lifetime = GSS_C_INDEFINITE;
++        } else {
++            now = time(NULL);
++            lifetime = now - ctx->expiryTime;
++            if (lifetime < 0)
++                lifetime = 0;
++        }
++
++        *lifetime_rec = lifetime;
++    }
++
++    if (mech_type != NULL) {
++        major = gssEapCanonicalizeOid(minor, ctx->mechanismUsed, 0, mech_type);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (ctx_flags != NULL) {
++        *ctx_flags = ctx->gssFlags;
++    }
++
++    if (locally_initiated != NULL) {
++        *locally_initiated = CTX_IS_INITIATOR(ctx);
++    }
++
++    if (open != NULL) {
++        *open = CTX_IS_ESTABLISHED(ctx);
++    }
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    if (GSS_ERROR(major)) {
++        gssEapReleaseName(&tmpMinor, src_name);
++        gssEapReleaseName(&tmpMinor, targ_name);
++    }
++
++    return major;
++}
+diff --git a/mech_eap/inquire_cred.c b/mech_eap/inquire_cred.c
+new file mode 100644
+index 0000000..227ab16
+--- /dev/null
++++ b/mech_eap/inquire_cred.c
+@@ -0,0 +1,61 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Return credential handle properties.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_cred(OM_uint32 *minor,
++                 gss_cred_id_t cred,
++                 gss_name_t *name,
++                 OM_uint32 *pLifetime,
++                 gss_cred_usage_t *cred_usage,
++                 gss_OID_set *mechanisms)
++{
++    OM_uint32 major;
++
++    if (cred == NULL) {
++        *minor = EINVAL;
++        return GSS_S_NO_CRED;
++    }
++
++    GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    major = gssEapInquireCred(minor, cred, name, pLifetime, cred_usage, mechanisms);
++
++    GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/inquire_cred_by_mech.c b/mech_eap/inquire_cred_by_mech.c
+new file mode 100644
+index 0000000..191902d
+--- /dev/null
++++ b/mech_eap/inquire_cred_by_mech.c
+@@ -0,0 +1,76 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Return credential handle properties.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_cred_by_mech(OM_uint32 *minor,
++                         gss_cred_id_t cred,
++                         gss_OID mech_type,
++                         gss_name_t *name,
++                         OM_uint32 *pInitiatorLifetime,
++                         OM_uint32 *pAcceptorLifetime,
++                         gss_cred_usage_t *cred_usage)
++{
++    OM_uint32 major, lifetime;
++
++    if (cred == NULL) {
++        *minor = EINVAL;
++        return GSS_S_NO_CRED;
++    }
++
++    GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    if (!gssEapCredAvailable(cred, mech_type)) {
++        major = GSS_S_BAD_MECH;
++        *minor = GSSEAP_CRED_MECH_MISMATCH;
++        goto cleanup;
++    }
++
++    major = gssEapInquireCred(minor, cred, name, &lifetime, cred_usage, NULL);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (pInitiatorLifetime != NULL)
++        *pInitiatorLifetime = (cred->flags & CRED_FLAG_INITIATE) ? lifetime : 0;
++    if (pAcceptorLifetime != NULL)
++        *pAcceptorLifetime = (cred->flags & CRED_FLAG_ACCEPT) ? lifetime : 0;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/inquire_cred_by_oid.c b/mech_eap/inquire_cred_by_oid.c
+new file mode 100644
+index 0000000..2ad34ed
+--- /dev/null
++++ b/mech_eap/inquire_cred_by_oid.c
+@@ -0,0 +1,83 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Return extended credential handle properties.
++ */
++
++#include "gssapiP_eap.h"
++
++#if 0
++static struct {
++    gss_OID_desc oid;
++    OM_uint32 (*inquire)(OM_uint32 *, const gss_cred_id_t,
++                         const gss_OID, gss_buffer_set_t *);
++} inquireCredOps[] = {
++};
++#endif
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_cred_by_oid(OM_uint32 *minor,
++                        const gss_cred_id_t cred_handle,
++                        const gss_OID desired_object GSSEAP_UNUSED,
++                        gss_buffer_set_t *data_set)
++{
++    OM_uint32 major;
++#if 0
++    int i;
++#endif
++    *data_set = GSS_C_NO_BUFFER_SET;
++
++    if (cred_handle == GSS_C_NO_CREDENTIAL) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED;
++    }
++
++    GSSEAP_MUTEX_LOCK(&cred_handle->mutex);
++
++    major = GSS_S_UNAVAILABLE;
++    *minor = GSSEAP_BAD_CRED_OPTION;
++
++#if 0
++    for (i = 0; i < sizeof(inquireCredOps) / sizeof(inquireCredOps[0]); i++) {
++        if (oidEqual(&inquireCredOps[i].oid, desired_object)) {
++            major = (*inquireCredOps[i].inquire)(minor, cred_handle,
++                                                 desired_object, data_set);
++            break;
++        }
++    }
++#endif
++
++    GSSEAP_MUTEX_UNLOCK(&cred_handle->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/inquire_mech_for_saslname.c b/mech_eap/inquire_mech_for_saslname.c
+new file mode 100644
+index 0000000..bd518c0
+--- /dev/null
++++ b/mech_eap/inquire_mech_for_saslname.c
+@@ -0,0 +1,84 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Map mechanism OID to a SASL mechanism name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_saslname_for_mech(OM_uint32 *minor,
++                              const gss_OID mech,
++                              gss_buffer_t sasl_mech_name,
++                              gss_buffer_t mech_name,
++                              gss_buffer_t mech_description)
++{
++    OM_uint32 major;
++    gss_buffer_t name;
++    krb5_enctype etype = ENCTYPE_NULL;
++
++    /* Dynamically construct mechanism name from Kerberos string enctype */
++    major = gssEapOidToEnctype(minor, mech, &etype);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (mech_name != GSS_C_NO_BUFFER) {
++        krb5_context krbContext;
++
++        GSSEAP_KRB_INIT(&krbContext);
++
++        *minor = krbEnctypeToString(krbContext, etype, "eap-", mech_name);
++        if (*minor != 0)
++            return GSS_S_FAILURE;
++    }
++
++    if (mech_description != GSS_C_NO_BUFFER) {
++        major = makeStringBuffer(minor,
++                                 "Extensible Authentication Protocol GSS-API Mechanism",
++                                 mech_description);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    if (sasl_mech_name != GSS_C_NO_BUFFER) {
++        name = gssEapOidToSaslName(mech);
++        if (name == GSS_C_NO_BUFFER) {
++            major = GSS_S_BAD_MECH;
++            *minor = GSSEAP_WRONG_MECH;
++        } else {
++            major = duplicateBuffer(minor, name, sasl_mech_name);
++        }
++    }
++
++    return major;
++}
+diff --git a/mech_eap/inquire_mechs_for_name.c b/mech_eap/inquire_mechs_for_name.c
+new file mode 100644
+index 0000000..89c869c
+--- /dev/null
++++ b/mech_eap/inquire_mechs_for_name.c
+@@ -0,0 +1,69 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Determine mechanism OIDs supported by name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_mechs_for_name(OM_uint32 *minor,
++                           const gss_name_t input_name,
++                           gss_OID_set *mech_types)
++{
++    OM_uint32 major, tmpMinor;
++
++    *minor = 0;
++    *mech_types = GSS_C_NO_OID_SET;
++
++    if (input_name != GSS_C_NO_NAME &&
++        input_name->mechanismUsed != GSS_C_NO_OID) {
++        major = gss_create_empty_oid_set(minor, mech_types);
++        if (GSS_ERROR(major))
++            return major;
++
++        major = gss_add_oid_set_member(minor,
++                                       input_name->mechanismUsed,
++                                       mech_types);
++        if (GSS_ERROR(major)) {
++            gss_release_oid_set(&tmpMinor, mech_types);
++            return major;
++        }
++    } else {
++        major = gssEapIndicateMechs(minor, mech_types);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    return major;
++}
+diff --git a/mech_eap/inquire_name.c b/mech_eap/inquire_name.c
+new file mode 100644
+index 0000000..78b08a0
+--- /dev/null
++++ b/mech_eap/inquire_name.c
+@@ -0,0 +1,75 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Enumerate name attributes.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_name(OM_uint32 *minor,
++                 gss_name_t name,
++                 int *name_is_MN,
++                 gss_OID *MN_mech,
++                 gss_buffer_set_t *attrs)
++{
++    OM_uint32 major, tmpMinor;
++
++    *minor = 0;
++
++    if (name_is_MN != NULL)
++        *name_is_MN = 0;
++    if (MN_mech != NULL)
++        *MN_mech = GSS_C_NO_OID;
++    if (attrs != NULL)
++        *attrs = GSS_C_NO_BUFFER_SET;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    if (attrs == NULL)
++        return GSS_S_COMPLETE;
++
++    GSSEAP_MUTEX_LOCK(&name->mutex);
++
++    major = gssEapInquireName(minor, name, name_is_MN, MN_mech, attrs);
++
++    GSSEAP_MUTEX_UNLOCK(&name->mutex);
++
++    if (GSS_ERROR(major))
++        gss_release_buffer_set(&tmpMinor, attrs);
++
++    return major;
++}
+diff --git a/mech_eap/inquire_names_for_mech.c b/mech_eap/inquire_names_for_mech.c
+new file mode 100644
+index 0000000..0e60340
+--- /dev/null
++++ b/mech_eap/inquire_names_for_mech.c
+@@ -0,0 +1,77 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Return supported name OID types.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_names_for_mech(OM_uint32 *minor,
++                           gss_OID mechanism,
++                           gss_OID_set *ret_name_types)
++{
++    OM_uint32 major, tmpMinor;
++    gss_OID nameTypes[] = {
++        GSS_C_NT_USER_NAME,
++        GSS_C_NT_HOSTBASED_SERVICE,
++        GSS_C_NT_EXPORT_NAME,
++#ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
++        GSS_C_NT_COMPOSITE_EXPORT,
++#endif
++        GSS_EAP_NT_EAP_NAME,
++        GSS_C_NT_ANONYMOUS,
++    };
++    size_t i;
++
++    if (!gssEapIsMechanismOid(mechanism)) {
++        *minor = GSSEAP_WRONG_MECH;
++        return GSS_S_BAD_MECH;
++    }
++
++    major = gss_create_empty_oid_set(minor, ret_name_types);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    for (i = 0; i < sizeof(nameTypes)/sizeof(nameTypes[0]); i++) {
++        major = gss_add_oid_set_member(minor, nameTypes[i], ret_name_types);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++cleanup:
++    if (GSS_ERROR(major))
++        gss_release_oid_set(&tmpMinor, ret_name_types);
++
++    return major;
++}
+diff --git a/mech_eap/inquire_saslname_for_mech.c b/mech_eap/inquire_saslname_for_mech.c
+new file mode 100644
+index 0000000..d6d7c14
+--- /dev/null
++++ b/mech_eap/inquire_saslname_for_mech.c
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Map SASL mechanism name to a mechanism OID.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_mech_for_saslname(OM_uint32 *minor,
++                              const gss_buffer_t sasl_mech_name,
++                              gss_OID *mech_type)
++{
++    *mech_type = gssEapSaslNameToOid(sasl_mech_name);
++    if (*mech_type == GSS_C_NO_OID) {
++        *minor = GSSEAP_WRONG_MECH;
++        return GSS_S_BAD_MECH;
++    }
++
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/inquire_sec_context_by_oid.c b/mech_eap/inquire_sec_context_by_oid.c
+new file mode 100644
+index 0000000..7435f2e
+--- /dev/null
++++ b/mech_eap/inquire_sec_context_by_oid.c
+@@ -0,0 +1,248 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Return extended properties of a context handle.
++ */
++
++#include "gssapiP_eap.h"
++
++static OM_uint32
++addEnctypeOidToBufferSet(OM_uint32 *minor,
++                         krb5_enctype encryptionType,
++                         gss_buffer_set_t *dataSet)
++{
++    OM_uint32 major;
++    unsigned char oidBuf[16];
++    gss_OID_desc oid;
++    gss_buffer_desc buf;
++
++    oid.length = sizeof(oidBuf);
++    oid.elements = oidBuf;
++
++    major = composeOid(minor,
++                       "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04",
++                       10,
++                       encryptionType,
++                       &oid);
++    if (GSS_ERROR(major))
++        return major;
++
++    buf.length = oid.length;
++    buf.value = oid.elements;
++
++    major = gss_add_buffer_set_member(minor, &buf, dataSet);
++
++    return major;
++}
++
++static void
++zeroAndReleaseBufferSet(gss_buffer_set_t *dataSet)
++{
++    OM_uint32 tmpMinor;
++    gss_buffer_set_t set = *dataSet;
++    size_t i;
++
++    if (set == GSS_C_NO_BUFFER_SET)
++        return;
++
++    for (i = 0; i <set->count; i++)
++        memset(set->elements[i].value, 0, set->elements[i].length);
++
++    gss_release_buffer_set(&tmpMinor, dataSet);
++}
++
++static OM_uint32
++inquireSessionKey(OM_uint32 *minor,
++                  const gss_ctx_id_t ctx,
++                  const gss_OID desired_object GSSEAP_UNUSED,
++                  gss_buffer_set_t *dataSet)
++{
++    OM_uint32 major;
++    gss_buffer_desc buf;
++
++    if (ctx->encryptionType == ENCTYPE_NULL) {
++        major = GSS_S_UNAVAILABLE;
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        goto cleanup;
++    }
++
++    buf.length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
++    buf.value = KRB_KEY_DATA(&ctx->rfc3961Key);
++
++    major = gss_add_buffer_set_member(minor, &buf, dataSet);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = addEnctypeOidToBufferSet(minor, ctx->encryptionType, dataSet);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major))
++        zeroAndReleaseBufferSet(dataSet);
++
++    return major;
++}
++
++static OM_uint32
++inquireNegoExKey(OM_uint32 *minor,
++                  const gss_ctx_id_t ctx,
++                  const gss_OID desired_object,
++                  gss_buffer_set_t *dataSet)
++{
++    OM_uint32 major, tmpMinor;
++    int bInitiatorKey;
++    gss_buffer_desc salt;
++    gss_buffer_desc key = GSS_C_EMPTY_BUFFER;
++    size_t keySize;
++
++    bInitiatorKey = CTX_IS_INITIATOR(ctx);
++
++    if (ctx->encryptionType == ENCTYPE_NULL) {
++        major = GSS_S_UNAVAILABLE;
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        goto cleanup;
++    }
++
++    /*
++     * If the caller supplied the verify key OID, then we need the acceptor
++     * key if we are the initiator, and vice versa.
++     */
++    if (desired_object->length == 11 &&
++        memcmp(desired_object->elements,
++               "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x07", 11) == 0)
++        bInitiatorKey ^= 1;
++
++    if (bInitiatorKey) {
++        salt.length = NEGOEX_INITIATOR_SALT_LEN;
++        salt.value  = NEGOEX_INITIATOR_SALT;
++    } else {
++        salt.length = NEGOEX_ACCEPTOR_SALT_LEN;
++        salt.value  = NEGOEX_ACCEPTOR_SALT;
++    }
++
++    keySize = KRB_KEY_LENGTH(&ctx->rfc3961Key);
++
++    major = gssEapPseudoRandom(minor, ctx, GSS_C_PRF_KEY_FULL, &salt,
++                               keySize, &key);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gss_add_buffer_set_member(minor, &key, dataSet);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = addEnctypeOidToBufferSet(minor, ctx->encryptionType, dataSet);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (key.value != NULL) {
++        memset(key.value, 0, key.length);
++        gss_release_buffer(&tmpMinor, &key);
++    }
++    if (GSS_ERROR(major))
++        zeroAndReleaseBufferSet(dataSet);
++
++    return major;
++}
++
++static struct {
++    gss_OID_desc oid;
++    OM_uint32 (*inquire)(OM_uint32 *, const gss_ctx_id_t,
++                         const gss_OID, gss_buffer_set_t *);
++} inquireCtxOps[] = {
++    {
++        /* GSS_C_INQ_SSPI_SESSION_KEY */
++        { 11, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" },
++        inquireSessionKey
++    },
++    {
++        /* GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT + v1 */
++        { 12, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x06\x01" },
++        gssEapExportLucidSecContext
++    },
++    {
++        /* GSS_C_INQ_NEGOEX_KEY */
++        { 11, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x06" },
++        inquireNegoExKey
++    },
++    {
++        /* GSS_C_INQ_NEGOEX_VERIFY_KEY */
++        { 11, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x07" },
++        inquireNegoExKey
++    },
++};
++
++OM_uint32 GSSAPI_CALLCONV
++gss_inquire_sec_context_by_oid(OM_uint32 *minor,
++                               const gss_ctx_id_t ctx,
++                               const gss_OID desired_object,
++                               gss_buffer_set_t *data_set)
++{
++    OM_uint32 major;
++    int i;
++
++    *data_set = GSS_C_NO_BUFFER_SET;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++#if 0
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        major = GSS_S_NO_CONTEXT;
++        goto cleanup;
++    }
++#endif
++
++    major = GSS_S_UNAVAILABLE;
++    *minor = GSSEAP_BAD_CONTEXT_OPTION;
++
++    for (i = 0; i < sizeof(inquireCtxOps) / sizeof(inquireCtxOps[0]); i++) {
++        if (oidEqual(&inquireCtxOps[i].oid, desired_object)) {
++            major = (*inquireCtxOps[i].inquire)(minor, ctx,
++                                                 desired_object, data_set);
++            break;
++        }
++    }
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/install-sh b/mech_eap/install-sh
+new file mode 100755
+index 0000000..6781b98
+--- /dev/null
++++ b/mech_eap/install-sh
+@@ -0,0 +1,520 @@
++#!/bin/sh
++# install - install a program, script, or datafile
++
++scriptversion=2009-04-28.21; # UTC
++
++# This originates from X11R5 (mit/util/scripts/install.sh), which was
++# later released in X11R6 (xc/config/util/install.sh) with the
++# following copyright and license.
++#
++# Copyright (C) 1994 X Consortium
++#
++# Permission is hereby granted, free of charge, to any person obtaining a copy
++# of this software and associated documentation files (the "Software"), to
++# deal in the Software without restriction, including without limitation the
++# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
++# sell copies of the Software, and to permit persons to whom the Software is
++# furnished to do so, subject to the following conditions:
++#
++# The above copyright notice and this permission notice shall be included in
++# all copies or substantial portions of the Software.
++#
++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
++# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
++# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++#
++# Except as contained in this notice, the name of the X Consortium shall not
++# be used in advertising or otherwise to promote the sale, use or other deal-
++# ings in this Software without prior written authorization from the X Consor-
++# tium.
++#
++#
++# FSF changes to this file are in the public domain.
++#
++# Calling this script install-sh is preferred over install.sh, to prevent
++# `make' implicit rules from creating a file called install from it
++# when there is no Makefile.
++#
++# This script is compatible with the BSD install script, but was written
++# from scratch.
++
++nl='
++'
++IFS=" ""      $nl"
++
++# set DOITPROG to echo to test this script
++
++# Don't use :- since 4.3BSD and earlier shells don't like it.
++doit=${DOITPROG-}
++if test -z "$doit"; then
++  doit_exec=exec
++else
++  doit_exec=$doit
++fi
++
++# Put in absolute file names if you don't have them in your path;
++# or use environment vars.
++
++chgrpprog=${CHGRPPROG-chgrp}
++chmodprog=${CHMODPROG-chmod}
++chownprog=${CHOWNPROG-chown}
++cmpprog=${CMPPROG-cmp}
++cpprog=${CPPROG-cp}
++mkdirprog=${MKDIRPROG-mkdir}
++mvprog=${MVPROG-mv}
++rmprog=${RMPROG-rm}
++stripprog=${STRIPPROG-strip}
++
++posix_glob='?'
++initialize_posix_glob='
++  test "$posix_glob" != "?" || {
++    if (set -f) 2>/dev/null; then
++      posix_glob=
++    else
++      posix_glob=:
++    fi
++  }
++'
++
++posix_mkdir=
++
++# Desired mode of installed file.
++mode=0755
++
++chgrpcmd=
++chmodcmd=$chmodprog
++chowncmd=
++mvcmd=$mvprog
++rmcmd="$rmprog -f"
++stripcmd=
++
++src=
++dst=
++dir_arg=
++dst_arg=
++
++copy_on_change=false
++no_target_directory=
++
++usage="\
++Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
++   or: $0 [OPTION]... SRCFILES... DIRECTORY
++   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
++   or: $0 [OPTION]... -d DIRECTORIES...
++
++In the 1st form, copy SRCFILE to DSTFILE.
++In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
++In the 4th, create DIRECTORIES.
++
++Options:
++     --help     display this help and exit.
++     --version  display version info and exit.
++
++  -c            (ignored)
++  -C            install only if different (preserve the last data modification time)
++  -d            create directories instead of installing files.
++  -g GROUP      $chgrpprog installed files to GROUP.
++  -m MODE       $chmodprog installed files to MODE.
++  -o USER       $chownprog installed files to USER.
++  -s            $stripprog installed files.
++  -t DIRECTORY  install into DIRECTORY.
++  -T            report an error if DSTFILE is a directory.
++
++Environment variables override the default commands:
++  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
++  RMPROG STRIPPROG
++"
++
++while test $# -ne 0; do
++  case $1 in
++    -c) ;;
++
++    -C) copy_on_change=true;;
++
++    -d) dir_arg=true;;
++
++    -g) chgrpcmd="$chgrpprog $2"
++      shift;;
++
++    --help) echo "$usage"; exit $?;;
++
++    -m) mode=$2
++      case $mode in
++        *' '* | *'    '* | *'
++'*      | *'*'* | *'?'* | *'['*)
++          echo "$0: invalid mode: $mode" >&2
++          exit 1;;
++      esac
++      shift;;
++
++    -o) chowncmd="$chownprog $2"
++      shift;;
++
++    -s) stripcmd=$stripprog;;
++
++    -t) dst_arg=$2
++      shift;;
++
++    -T) no_target_directory=true;;
++
++    --version) echo "$0 $scriptversion"; exit $?;;
++
++    --)       shift
++      break;;
++
++    -*)       echo "$0: invalid option: $1" >&2
++      exit 1;;
++
++    *)  break;;
++  esac
++  shift
++done
++
++if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
++  # When -d is used, all remaining arguments are directories to create.
++  # When -t is used, the destination is already specified.
++  # Otherwise, the last argument is the destination.  Remove it from $@.
++  for arg
++  do
++    if test -n "$dst_arg"; then
++      # $@ is not empty: it contains at least $arg.
++      set fnord "$@" "$dst_arg"
++      shift # fnord
++    fi
++    shift # arg
++    dst_arg=$arg
++  done
++fi
++
++if test $# -eq 0; then
++  if test -z "$dir_arg"; then
++    echo "$0: no input file specified." >&2
++    exit 1
++  fi
++  # It's OK to call `install-sh -d' without argument.
++  # This can happen when creating conditional directories.
++  exit 0
++fi
++
++if test -z "$dir_arg"; then
++  trap '(exit $?); exit' 1 2 13 15
++
++  # Set umask so as not to create temps with too-generous modes.
++  # However, 'strip' requires both read and write access to temps.
++  case $mode in
++    # Optimize common cases.
++    *644) cp_umask=133;;
++    *755) cp_umask=22;;
++
++    *[0-7])
++      if test -z "$stripcmd"; then
++      u_plus_rw=
++      else
++      u_plus_rw='% 200'
++      fi
++      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
++    *)
++      if test -z "$stripcmd"; then
++      u_plus_rw=
++      else
++      u_plus_rw=,u+rw
++      fi
++      cp_umask=$mode$u_plus_rw;;
++  esac
++fi
++
++for src
++do
++  # Protect names starting with `-'.
++  case $src in
++    -*) src=./$src;;
++  esac
++
++  if test -n "$dir_arg"; then
++    dst=$src
++    dstdir=$dst
++    test -d "$dstdir"
++    dstdir_status=$?
++  else
++
++    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
++    # might cause directories to be created, which would be especially bad
++    # if $src (and thus $dsttmp) contains '*'.
++    if test ! -f "$src" && test ! -d "$src"; then
++      echo "$0: $src does not exist." >&2
++      exit 1
++    fi
++
++    if test -z "$dst_arg"; then
++      echo "$0: no destination specified." >&2
++      exit 1
++    fi
++
++    dst=$dst_arg
++    # Protect names starting with `-'.
++    case $dst in
++      -*) dst=./$dst;;
++    esac
++
++    # If destination is a directory, append the input filename; won't work
++    # if double slashes aren't ignored.
++    if test -d "$dst"; then
++      if test -n "$no_target_directory"; then
++      echo "$0: $dst_arg: Is a directory" >&2
++      exit 1
++      fi
++      dstdir=$dst
++      dst=$dstdir/`basename "$src"`
++      dstdir_status=0
++    else
++      # Prefer dirname, but fall back on a substitute if dirname fails.
++      dstdir=`
++      (dirname "$dst") 2>/dev/null ||
++      expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
++           X"$dst" : 'X\(//\)[^/]' \| \
++           X"$dst" : 'X\(//\)$' \| \
++           X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
++      echo X"$dst" |
++          sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
++                 s//\1/
++                 q
++               }
++               /^X\(\/\/\)[^/].*/{
++                 s//\1/
++                 q
++               }
++               /^X\(\/\/\)$/{
++                 s//\1/
++                 q
++               }
++               /^X\(\/\).*/{
++                 s//\1/
++                 q
++               }
++               s/.*/./; q'
++      `
++
++      test -d "$dstdir"
++      dstdir_status=$?
++    fi
++  fi
++
++  obsolete_mkdir_used=false
++
++  if test $dstdir_status != 0; then
++    case $posix_mkdir in
++      '')
++      # Create intermediate dirs using mode 755 as modified by the umask.
++      # This is like FreeBSD 'install' as of 1997-10-28.
++      umask=`umask`
++      case $stripcmd.$umask in
++        # Optimize common cases.
++        *[2367][2367]) mkdir_umask=$umask;;
++        .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
++
++        *[0-7])
++          mkdir_umask=`expr $umask + 22 \
++            - $umask % 100 % 40 + $umask % 20 \
++            - $umask % 10 % 4 + $umask % 2
++          `;;
++        *) mkdir_umask=$umask,go-w;;
++      esac
++
++      # With -d, create the new directory with the user-specified mode.
++      # Otherwise, rely on $mkdir_umask.
++      if test -n "$dir_arg"; then
++        mkdir_mode=-m$mode
++      else
++        mkdir_mode=
++      fi
++
++      posix_mkdir=false
++      case $umask in
++        *[123567][0-7][0-7])
++          # POSIX mkdir -p sets u+wx bits regardless of umask, which
++          # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
++          ;;
++        *)
++          tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
++          trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
++
++          if (umask $mkdir_umask &&
++              exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
++          then
++            if test -z "$dir_arg" || {
++                 # Check for POSIX incompatibilities with -m.
++                 # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
++                 # other-writeable bit of parent directory when it shouldn't.
++                 # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
++                 ls_ld_tmpdir=`ls -ld "$tmpdir"`
++                 case $ls_ld_tmpdir in
++                   d????-?r-*) different_mode=700;;
++                   d????-?--*) different_mode=755;;
++                   *) false;;
++                 esac &&
++                 $mkdirprog -m$different_mode -p -- "$tmpdir" && {
++                   ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
++                   test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
++                 }
++               }
++            then posix_mkdir=:
++            fi
++            rmdir "$tmpdir/d" "$tmpdir"
++          else
++            # Remove any dirs left behind by ancient mkdir implementations.
++            rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
++          fi
++          trap '' 0;;
++      esac;;
++    esac
++
++    if
++      $posix_mkdir && (
++      umask $mkdir_umask &&
++      $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
++      )
++    then :
++    else
++
++      # The umask is ridiculous, or mkdir does not conform to POSIX,
++      # or it failed possibly due to a race condition.  Create the
++      # directory the slow way, step by step, checking for races as we go.
++
++      case $dstdir in
++      /*) prefix='/';;
++      -*) prefix='./';;
++      *)  prefix='';;
++      esac
++
++      eval "$initialize_posix_glob"
++
++      oIFS=$IFS
++      IFS=/
++      $posix_glob set -f
++      set fnord $dstdir
++      shift
++      $posix_glob set +f
++      IFS=$oIFS
++
++      prefixes=
++
++      for d
++      do
++      test -z "$d" && continue
++
++      prefix=$prefix$d
++      if test -d "$prefix"; then
++        prefixes=
++      else
++        if $posix_mkdir; then
++          (umask=$mkdir_umask &&
++           $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
++          # Don't fail if two instances are running concurrently.
++          test -d "$prefix" || exit 1
++        else
++          case $prefix in
++            *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
++            *) qprefix=$prefix;;
++          esac
++          prefixes="$prefixes '$qprefix'"
++        fi
++      fi
++      prefix=$prefix/
++      done
++
++      if test -n "$prefixes"; then
++      # Don't fail if two instances are running concurrently.
++      (umask $mkdir_umask &&
++       eval "\$doit_exec \$mkdirprog $prefixes") ||
++        test -d "$dstdir" || exit 1
++      obsolete_mkdir_used=true
++      fi
++    fi
++  fi
++
++  if test -n "$dir_arg"; then
++    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
++    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
++    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
++      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
++  else
++
++    # Make a couple of temp file names in the proper directory.
++    dsttmp=$dstdir/_inst.$$_
++    rmtmp=$dstdir/_rm.$$_
++
++    # Trap to clean up those temp files at exit.
++    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
++
++    # Copy the file name to the temp name.
++    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
++
++    # and set any options; do chmod last to preserve setuid bits.
++    #
++    # If any of these fail, we abort the whole thing.  If we want to
++    # ignore errors from any of these, just make sure not to ignore
++    # errors from the above "$doit $cpprog $src $dsttmp" command.
++    #
++    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
++    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
++    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
++    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
++
++    # If -C, don't bother to copy if it wouldn't change the file.
++    if $copy_on_change &&
++       old=`LC_ALL=C ls -dlL "$dst"   2>/dev/null` &&
++       new=`LC_ALL=C ls -dlL "$dsttmp"        2>/dev/null` &&
++
++       eval "$initialize_posix_glob" &&
++       $posix_glob set -f &&
++       set X $old && old=:$2:$4:$5:$6 &&
++       set X $new && new=:$2:$4:$5:$6 &&
++       $posix_glob set +f &&
++
++       test "$old" = "$new" &&
++       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
++    then
++      rm -f "$dsttmp"
++    else
++      # Rename the file to the real destination.
++      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
++
++      # The rename failed, perhaps because mv can't rename something else
++      # to itself, or perhaps because mv is so ancient that it does not
++      # support -f.
++      {
++      # Now remove or move aside any old file at destination location.
++      # We try this two ways since rm can't unlink itself on some
++      # systems and the destination file might be busy for other
++      # reasons.  In this case, the final cleanup might fail but the new
++      # file should still install successfully.
++      {
++        test ! -f "$dst" ||
++        $doit $rmcmd -f "$dst" 2>/dev/null ||
++        { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
++          { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
++        } ||
++        { echo "$0: cannot unlink or rename $dst" >&2
++          (exit 1); exit 1
++        }
++      } &&
++
++      # Now rename the file to the real destination.
++      $doit $mvcmd "$dsttmp" "$dst"
++      }
++    fi || exit 1
++
++    trap '' 0
++  fi
++done
++
++# Local variables:
++# eval: (add-hook 'write-file-hooks 'time-stamp)
++# time-stamp-start: "scriptversion="
++# time-stamp-format: "%:y-%02m-%02d.%02H"
++# time-stamp-time-zone: "UTC"
++# time-stamp-end: "; # UTC"
++# End:
+diff --git a/mech_eap/map_name_to_any.c b/mech_eap/map_name_to_any.c
+new file mode 100644
+index 0000000..2a8a96c
+--- /dev/null
++++ b/mech_eap/map_name_to_any.c
+@@ -0,0 +1,58 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_map_name_to_any(OM_uint32 *minor,
++                    gss_name_t name,
++                    int authenticated,
++                    gss_buffer_t type_id,
++                    gss_any_t *output)
++{
++    OM_uint32 major;
++
++    *output = (gss_any_t)NULL;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&name->mutex);
++
++    major = gssEapMapNameToAny(minor, name, authenticated, type_id, output);
++
++    GSSEAP_MUTEX_UNLOCK(&name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/mech b/mech_eap/mech
+new file mode 100644
+index 0000000..258a43a
+--- /dev/null
++++ b/mech_eap/mech
+@@ -0,0 +1,8 @@
++#
++# Sample mechanism glue configuration for EAP GSS mechanism.
++#
++# Any encryption type supported by Kerberos can be defined as the
++# last element of the OID arc.
++#
++eap-aes128            1.3.6.1.4.1.5322.22.1.17        mech_eap.so
++eap-aes256            1.3.6.1.4.1.5322.22.1.18        mech_eap.so
+diff --git a/mech_eap/mech_eap-noacceptor.exports b/mech_eap/mech_eap-noacceptor.exports
+new file mode 100644
+index 0000000..f00df8a
+--- /dev/null
++++ b/mech_eap/mech_eap-noacceptor.exports
+@@ -0,0 +1,55 @@
++gss_acquire_cred
++gss_add_cred
++gss_add_cred_with_password
++gss_canonicalize_name
++gss_compare_name
++gss_context_time
++gss_delete_sec_context
++gss_display_name
++gss_display_name_ext
++gss_display_status
++gss_duplicate_name
++gss_exchange_meta_data
++gss_export_name
++gss_export_sec_context
++gss_get_mic
++gss_import_name
++gss_import_sec_context
++gss_indicate_mechs
++gss_init_sec_context
++gss_inquire_attrs_for_mech
++gss_inquire_context
++gss_inquire_cred
++gss_inquire_cred_by_mech
++gss_inquire_cred_by_oid
++gss_inquire_mechs_for_name
++gss_inquire_mech_for_saslname
++gss_inquire_names_for_mech
++gss_inquire_saslname_for_mech
++gss_inquire_sec_context_by_oid
++gss_process_context_token
++gss_pseudo_random
++gss_query_mechanism_info
++gss_query_meta_data
++gss_release_cred
++gss_release_name
++gss_internal_release_oid
++gss_set_sec_context_option
++gss_store_cred
++gss_unwrap
++gss_unwrap_iov
++gss_verify_mic
++gss_wrap
++gss_wrap_iov
++gss_wrap_iov_length
++gss_wrap_size_limit
++GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM
++GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM
++GSS_EAP_NT_EAP_NAME
++GSS_EAP_CRED_SET_CRED_FLAG
++GSS_EAP_CRED_SET_CRED_PASSWORD
++GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE
++GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA
++gssspi_acquire_cred_with_password
++gssspi_authorize_localname
++gssspi_set_cred_option
+diff --git a/mech_eap/mech_eap.exports b/mech_eap/mech_eap.exports
+new file mode 100644
+index 0000000..6a17a17
+--- /dev/null
++++ b/mech_eap/mech_eap.exports
+@@ -0,0 +1,63 @@
++gss_accept_sec_context
++gss_acquire_cred
++gss_add_cred
++gss_add_cred_with_password
++gss_canonicalize_name
++gss_compare_name
++gss_context_time
++gss_delete_name_attribute
++gss_delete_sec_context
++gss_display_name
++gss_display_name_ext
++gss_display_status
++gss_duplicate_name
++gss_exchange_meta_data
++gss_export_name
++gss_export_name_composite
++gss_export_sec_context
++gss_get_mic
++gss_get_name_attribute
++gss_import_name
++gss_import_sec_context
++gss_indicate_mechs
++gss_init_sec_context
++gss_inquire_attrs_for_mech
++gss_inquire_context
++gss_inquire_cred
++gss_inquire_cred_by_mech
++gss_inquire_cred_by_oid
++gss_inquire_mechs_for_name
++gss_inquire_mech_for_saslname
++gss_inquire_name
++gss_inquire_names_for_mech
++gss_inquire_saslname_for_mech
++gss_inquire_sec_context_by_oid
++gss_map_name_to_any
++gss_process_context_token
++gss_pseudo_random
++gss_query_mechanism_info
++gss_query_meta_data
++gss_release_any_name_mapping
++gss_release_cred
++gss_release_name
++gss_internal_release_oid
++gss_set_name_attribute
++gss_set_sec_context_option
++gss_store_cred
++gss_unwrap
++gss_unwrap_iov
++gss_verify_mic
++gss_wrap
++gss_wrap_iov
++gss_wrap_iov_length
++gss_wrap_size_limit
++GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM
++GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM
++GSS_EAP_NT_EAP_NAME
++GSS_EAP_CRED_SET_CRED_FLAG
++GSS_EAP_CRED_SET_CRED_PASSWORD
++GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE
++GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA
++gssspi_acquire_cred_with_password
++gssspi_authorize_localname
++gssspi_set_cred_option
+diff --git a/mech_eap/mech_invoke.c b/mech_eap/mech_invoke.c
+new file mode 100644
+index 0000000..bc9bba3
+--- /dev/null
++++ b/mech_eap/mech_invoke.c
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gssspi_mech_invoke(OM_uint32 *minor,
++                   const gss_OID desired_mech,
++                   const gss_OID desired_object,
++                   gss_buffer_t value)
++{
++    *minor = GSSEAP_BAD_INVOCATION;
++
++    return GSS_S_UNAVAILABLE;
++}
+diff --git a/mech_eap/process_context_token.c b/mech_eap/process_context_token.c
+new file mode 100644
+index 0000000..02a4b6d
+--- /dev/null
++++ b/mech_eap/process_context_token.c
+@@ -0,0 +1,71 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_process_context_token(OM_uint32 *minor,
++                          gss_ctx_id_t ctx,
++                          gss_buffer_t token_buffer)
++{
++    OM_uint32 major;
++    gss_iov_buffer_desc iov[1];
++
++    *minor = 0;
++
++    if (ctx == NULL) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        return GSS_S_NO_CONTEXT;
++    }
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
++    iov[0].buffer = *token_buffer;
++
++    major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
++                                    iov, 1, TOK_TYPE_DELETE_CONTEXT);
++    if (GSS_ERROR(major)) {
++        GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++        return major;
++    }
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return gssEapReleaseContext(minor, &ctx);
++}
+diff --git a/mech_eap/pseudo_random.c b/mech_eap/pseudo_random.c
+new file mode 100644
+index 0000000..61d1f2a
+--- /dev/null
++++ b/mech_eap/pseudo_random.c
+@@ -0,0 +1,195 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 2009 by the Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ */
++
++/*
++ * PRF
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++gssEapPseudoRandom(OM_uint32 *minor,
++                   gss_ctx_id_t ctx,
++                   int prf_key,
++                   const gss_buffer_t prf_in,
++                   ssize_t desired_output_len,
++                   gss_buffer_t prf_out)
++{
++    krb5_error_code code;
++    int i;
++    OM_uint32 tmpMinor;
++    size_t prflen;
++    krb5_data t, ns;
++    unsigned char *p;
++    krb5_context krbContext;
++
++    prf_out->length = 0;
++    prf_out->value = NULL;
++
++    *minor = 0;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    KRB_DATA_INIT(&t);
++    KRB_DATA_INIT(&ns);
++
++    if (prf_key != GSS_C_PRF_KEY_PARTIAL &&
++        prf_key != GSS_C_PRF_KEY_FULL) {
++        code = GSSEAP_BAD_PRF_KEY;
++        goto cleanup;
++    }
++
++    prf_out->value = GSSEAP_MALLOC(desired_output_len);
++    if (prf_out->value == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++    prf_out->length = desired_output_len;
++
++    code = krb5_c_prf_length(krbContext,
++                             ctx->encryptionType,
++                             &prflen);
++    if (code != 0)
++        goto cleanup;
++
++    ns.length = 4 + prf_in->length;
++    ns.data = GSSEAP_MALLOC(ns.length);
++    if (ns.data == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++
++#ifndef HAVE_HEIMDAL_VERSION
++    /* Same API, but different allocation rules, unfortunately. */
++    t.length = prflen;
++    t.data = GSSEAP_MALLOC(t.length);
++    if (t.data == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++#endif
++
++    memcpy((unsigned char *)ns.data + 4, prf_in->value, prf_in->length);
++    i = 0;
++    p = (unsigned char *)prf_out->value;
++    while (desired_output_len > 0) {
++        store_uint32_be(i, ns.data);
++
++        code = krb5_c_prf(krbContext, &ctx->rfc3961Key, &ns, &t);
++        if (code != 0)
++            goto cleanup;
++
++        memcpy(p, t.data, MIN(t.length, desired_output_len));
++
++        p += t.length;
++        desired_output_len -= t.length;
++        i++;
++    }
++
++cleanup:
++    if (code != 0)
++        gss_release_buffer(&tmpMinor, prf_out);
++    if (ns.data != NULL) {
++        memset(ns.data, 0, ns.length);
++        GSSEAP_FREE(ns.data);
++    }
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_free_data_contents(krbContext, &t);
++#else
++    if (t.data != NULL) {
++        memset(t.data, 0, t.length);
++        GSSEAP_FREE(t.data);
++    }
++#endif
++
++    *minor = code;
++
++    return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_pseudo_random(OM_uint32 *minor,
++                  gss_ctx_id_t ctx,
++                  int prf_key,
++                  const gss_buffer_t prf_in,
++                  ssize_t desired_output_len,
++                  gss_buffer_t prf_out)
++{
++    OM_uint32 major;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    prf_out->length = 0;
++    prf_out->value = NULL;
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (CTX_IS_ESTABLISHED(ctx)) {
++        major = gssEapPseudoRandom(minor, ctx, prf_key,
++                                   prf_in, desired_output_len, prf_out);
++    } else {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++    }
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/query_mechanism_info.c b/mech_eap/query_mechanism_info.c
+new file mode 100644
+index 0000000..acd3115
+--- /dev/null
++++ b/mech_eap/query_mechanism_info.c
+@@ -0,0 +1,67 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ *
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++gssQueryMechanismInfo(OM_uint32 *minor,
++                      gss_const_OID mech_oid,
++                      unsigned char auth_scheme[16])
++{
++    OM_uint32 major;
++    krb5_enctype enctype;
++
++    major = gssEapOidToEnctype(minor, (const gss_OID)mech_oid, &enctype);
++    if (GSS_ERROR(major))
++        return major;
++
++    /* the enctype is encoded in the increasing part of the GUID */
++    memcpy(auth_scheme,
++           "\x39\xd7\x7d\x00\xe5\x00\x11\xe0\xac\x64\xcd\x53\x46\x50\xac\xb9", 16);
++
++    auth_scheme[3] = (unsigned char)enctype;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_query_mechanism_info(OM_uint32 *minor,
++                         gss_const_OID mech_oid,
++                         unsigned char auth_scheme[16])
++{
++    return gssQueryMechanismInfo(minor, mech_oid, auth_scheme);
++}
+diff --git a/mech_eap/query_meta_data.c b/mech_eap/query_meta_data.c
+new file mode 100644
+index 0000000..abc7e71
+--- /dev/null
++++ b/mech_eap/query_meta_data.c
+@@ -0,0 +1,116 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ *
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++gssEapQueryMetaData(OM_uint32 *minor,
++                    gss_const_OID mech GSSEAP_UNUSED,
++                    gss_cred_id_t cred,
++                    gss_ctx_id_t *context_handle,
++                    const gss_name_t name,
++                    OM_uint32 req_flags GSSEAP_UNUSED,
++                    gss_buffer_t meta_data)
++{
++    OM_uint32 major = GSS_S_COMPLETE;
++    int isInitiator = (name != GSS_C_NO_NAME);
++    gss_ctx_id_t ctx = *context_handle;
++
++    meta_data->length = 0;
++    meta_data->value = NULL;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        major = gssEapAllocContext(minor, &ctx);
++        if (GSS_ERROR(major))
++            return major;
++
++        if (isInitiator)
++            ctx->flags |= CTX_FLAG_INITIATOR;
++    }
++
++    if (ctx->cred == GSS_C_NO_CREDENTIAL) {
++        if (isInitiator) {
++            major = gssEapResolveInitiatorCred(minor, cred,
++                                               name, &ctx->cred);
++        } else {
++            major = gssEapAcquireCred(minor,
++                                      GSS_C_NO_NAME,
++                                      GSS_C_INDEFINITE,
++                                      GSS_C_NO_OID_SET,
++                                      GSS_C_ACCEPT,
++                                      &ctx->cred,
++                                      NULL,
++                                      NULL);
++        }
++    }
++
++    if (*context_handle == GSS_C_NO_CONTEXT)
++        *context_handle = ctx;
++
++    return major;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_query_meta_data(OM_uint32 *minor,
++                    gss_const_OID mech,
++                    gss_cred_id_t cred,
++                    gss_ctx_id_t *context_handle,
++                    const gss_name_t name,
++                    OM_uint32 req_flags,
++                    gss_buffer_t meta_data)
++{
++    gss_ctx_id_t ctx = *context_handle;
++    OM_uint32 major;
++
++    if (cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    if (*context_handle != GSS_C_NO_CONTEXT)
++        GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    major = gssEapQueryMetaData(minor, mech, cred, &ctx,
++                                name, req_flags, meta_data);
++
++    if (*context_handle != GSS_C_NO_CONTEXT)
++        GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++    else
++        *context_handle = ctx;
++
++    if (cred != GSS_C_NO_CREDENTIAL)
++        GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/radius_ad.exports b/mech_eap/radius_ad.exports
+new file mode 100644
+index 0000000..8d5d5c4
+--- /dev/null
++++ b/mech_eap/radius_ad.exports
+@@ -0,0 +1 @@
++authdata_client_0
+diff --git a/mech_eap/radsec.conf b/mech_eap/radsec.conf
+new file mode 100644
+index 0000000..27f895a
+--- /dev/null
++++ b/mech_eap/radsec.conf
+@@ -0,0 +1,12 @@
++dictionary = "/usr/local/etc/raddb/dictionary"
++
++realm gss-eap {
++    type = "UDP"
++    timeout = 5
++    retries = 3
++    server {
++        hostname = "localhost"
++        service = "1812"
++        secret = "testing123"
++    }
++}
+diff --git a/mech_eap/radsec_err.et b/mech_eap/radsec_err.et
+new file mode 100644
+index 0000000..3b7fae2
+--- /dev/null
++++ b/mech_eap/radsec_err.et
+@@ -0,0 +1,38 @@
++#
++# Copyright (c) 2011, JANET(UK)
++#  All rights reserved.
++# 
++#  Redistribution and use in source and binary forms, with or without
++#  modification, are permitted provided that the following conditions
++#  are met:
++# 
++#  1. Redistributions of source code must retain the above copyright
++#     notice, this list of conditions and the following disclaimer.
++# 
++#  2. Redistributions in binary form must reproduce the above copyright
++#     notice, this list of conditions and the following disclaimer in the
++#     documentation and/or other materials provided with the distribution.
++# 
++#  3. Neither the name of JANET(UK) nor the names of its contributors
++#     may be used to endorse or promote products derived from this software
++#     without specific prior written permission.
++# 
++#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++#  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++#  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++#  SUCH DAMAGE.
++#
++
++# Placeholders only
++error_table rse
++
++error_code GSSEAP_RSE_OK,                       ""
++
++end
+diff --git a/mech_eap/release_any_name_mapping.c b/mech_eap/release_any_name_mapping.c
+new file mode 100644
+index 0000000..d68fb45
+--- /dev/null
++++ b/mech_eap/release_any_name_mapping.c
+@@ -0,0 +1,59 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_release_any_name_mapping(OM_uint32 *minor,
++                             gss_name_t name,
++                             gss_buffer_t type_id,
++                             gss_any_t *input)
++{
++    OM_uint32 major;
++
++    *minor = 0;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&name->mutex);
++
++    major = gssEapReleaseAnyNameMapping(minor, name, type_id, input);
++
++    *input = NULL;
++
++    GSSEAP_MUTEX_UNLOCK(&name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/release_cred.c b/mech_eap/release_cred.c
+new file mode 100644
+index 0000000..8bb7e54
+--- /dev/null
++++ b/mech_eap/release_cred.c
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Release a credential handle.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_release_cred(OM_uint32 *minor,
++                 gss_cred_id_t *cred_handle)
++{
++    return gssEapReleaseCred(minor, cred_handle);
++}
+diff --git a/mech_eap/release_name.c b/mech_eap/release_name.c
+new file mode 100644
+index 0000000..3d527ce
+--- /dev/null
++++ b/mech_eap/release_name.c
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Release a name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_release_name(OM_uint32 *minor,
++                 gss_name_t *name)
++{
++    return gssEapReleaseName(minor, name);
++}
+diff --git a/mech_eap/release_oid.c b/mech_eap/release_oid.c
+new file mode 100644
+index 0000000..291da40
+--- /dev/null
++++ b/mech_eap/release_oid.c
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Mark an internalized OID as not required to be released.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_internal_release_oid(OM_uint32 *minor,
++                         gss_OID *oid)
++{
++    return gssEapReleaseOid(minor, oid);
++}
+diff --git a/mech_eap/set_cred_option.c b/mech_eap/set_cred_option.c
+new file mode 100644
+index 0000000..7bb9b7b
+--- /dev/null
++++ b/mech_eap/set_cred_option.c
+@@ -0,0 +1,208 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Set an extended property on a credential handle.
++ */
++
++#include "gssapiP_eap.h"
++
++static OM_uint32
++setCredRadiusConfigFile(OM_uint32 *minor,
++                        gss_cred_id_t cred,
++                        const gss_OID oid GSSEAP_UNUSED,
++                        const gss_buffer_t buffer)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc configFileBuffer = GSS_C_EMPTY_BUFFER;
++
++    if (buffer != GSS_C_NO_BUFFER && buffer->length != 0) {
++        major = duplicateBuffer(minor, buffer, &configFileBuffer);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    gss_release_buffer(&tmpMinor, &cred->radiusConfigFile);
++    cred->radiusConfigFile = configFileBuffer;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++setCredRadiusConfigStanza(OM_uint32 *minor,
++                          gss_cred_id_t cred,
++                          const gss_OID oid GSSEAP_UNUSED,
++                          const gss_buffer_t buffer)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc configStanzaBuffer = GSS_C_EMPTY_BUFFER;
++
++    if (buffer != GSS_C_NO_BUFFER && buffer->length != 0) {
++        major = duplicateBuffer(minor, buffer, &configStanzaBuffer);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    gss_release_buffer(&tmpMinor, &cred->radiusConfigStanza);
++    cred->radiusConfigStanza = configStanzaBuffer;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++setCredFlag(OM_uint32 *minor,
++            gss_cred_id_t cred,
++            const gss_OID oid GSSEAP_UNUSED,
++            const gss_buffer_t buffer)
++{
++    OM_uint32 flags;
++    unsigned char *p;
++
++    if (buffer == GSS_C_NO_BUFFER) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_FAILURE;
++    }
++
++    if (buffer->length < 4) {
++        *minor = GSSEAP_WRONG_SIZE;
++        return GSS_S_FAILURE;
++    }
++
++    p = (unsigned char *)buffer->value;
++
++    flags = load_uint32_be(buffer->value) & CRED_FLAG_PUBLIC_MASK;
++
++    if (buffer->length > 4 && p[4])
++        cred->flags &= ~(flags);
++    else
++        cred->flags |= flags;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++setCredPassword(OM_uint32 *minor,
++                gss_cred_id_t cred,
++                const gss_OID oid GSSEAP_UNUSED,
++                const gss_buffer_t buffer)
++{
++    return gssEapSetCredPassword(minor, cred, buffer);
++}
++
++static struct {
++    gss_OID_desc oid;
++    OM_uint32 (*setOption)(OM_uint32 *, gss_cred_id_t cred,
++                           const gss_OID, const gss_buffer_t);
++} setCredOps[] = {
++    /* 1.3.6.1.4.1.5322.22.3.3.1 */
++    {
++        { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x01" },
++        setCredRadiusConfigFile,
++    },
++    /* 1.3.6.1.4.1.5322.22.3.3.2 */
++    {
++        { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x02" },
++        setCredRadiusConfigStanza,
++    },
++    /* 1.3.6.1.4.1.5322.22.3.3.3 */
++    {
++        { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x03" },
++        setCredFlag,
++    },
++    /* 1.3.6.1.4.1.5322.22.3.3.4 */
++    {
++        { 11, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x03\x03\x04" },
++        setCredPassword,
++    },
++};
++
++gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_FILE     = &setCredOps[0].oid;
++gss_OID GSS_EAP_CRED_SET_RADIUS_CONFIG_STANZA   = &setCredOps[1].oid;
++gss_OID GSS_EAP_CRED_SET_CRED_FLAG              = &setCredOps[2].oid;
++gss_OID GSS_EAP_CRED_SET_CRED_PASSWORD          = &setCredOps[3].oid;
++
++OM_uint32 GSSAPI_CALLCONV
++gssspi_set_cred_option(OM_uint32 *minor,
++                       gss_cred_id_t *pCred,
++                       const gss_OID desired_object,
++                       const gss_buffer_t value)
++{
++    OM_uint32 major;
++    gss_cred_id_t cred = *pCred;
++    int i;
++
++    if (cred == GSS_C_NO_CREDENTIAL) {
++        *minor = EINVAL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    major = GSS_S_UNAVAILABLE;
++    *minor = GSSEAP_BAD_CRED_OPTION;
++
++    for (i = 0; i < sizeof(setCredOps) / sizeof(setCredOps[0]); i++) {
++        if (oidEqual(&setCredOps[i].oid, desired_object)) {
++            major = (*setCredOps[i].setOption)(minor, cred,
++                                               desired_object, value);
++            break;
++        }
++    }
++
++    GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++
++    return major;
++}
++
++#if 0
++OM_uint32
++gsseap_set_cred_flag(OM_uint32 *minor,
++                     gss_cred_id_t cred,
++                     OM_uint32 flag,
++                     int clear)
++{
++    unsigned char buf[5];
++    gss_buffer_desc value;
++
++    value.length = sizeof(buf);
++    value.value = buf;
++
++    store_uint32_be(flag, buf);
++    buf[4] = (clear != 0);
++
++    return gssspi_set_cred_option(minor, &cred,
++                                  GSS_EAP_CRED_SET_CRED_FLAG, &value);
++}
++#endif
+diff --git a/mech_eap/set_name_attribute.c b/mech_eap/set_name_attribute.c
+new file mode 100644
+index 0000000..2ccf5d7
+--- /dev/null
++++ b/mech_eap/set_name_attribute.c
+@@ -0,0 +1,60 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Set an attribute on a name.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_set_name_attribute(OM_uint32 *minor,
++                       gss_name_t name,
++                       int complete,
++                       gss_buffer_t attr,
++                       gss_buffer_t value)
++{
++    OM_uint32 major;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_MUTEX_LOCK(&name->mutex);
++
++    major = gssEapSetNameAttribute(minor, name, complete, attr, value);
++
++    GSSEAP_MUTEX_UNLOCK(&name->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/set_sec_context_option.c b/mech_eap/set_sec_context_option.c
+new file mode 100644
+index 0000000..f9fa3a6
+--- /dev/null
++++ b/mech_eap/set_sec_context_option.c
+@@ -0,0 +1,87 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Set an extended property on a context handle.
++ */
++
++#include "gssapiP_eap.h"
++
++#if 0
++static struct {
++    gss_OID_desc oid;
++    OM_uint32 (*setOption)(OM_uint32 *, gss_ctx_id_t *pCtx,
++                           const gss_OID, const gss_buffer_t);
++} setCtxOps[] = {
++};
++#endif
++
++OM_uint32 GSSAPI_CALLCONV
++gss_set_sec_context_option(OM_uint32 *minor,
++                           gss_ctx_id_t *pCtx,
++                           const gss_OID desired_object GSSEAP_UNUSED,
++                           const gss_buffer_t value GSSEAP_UNUSED)
++{
++    OM_uint32 major;
++    gss_ctx_id_t ctx;
++#if 0
++    int i;
++#endif
++
++    major = GSS_S_UNAVAILABLE;
++    *minor = GSSEAP_BAD_CONTEXT_OPTION;
++
++    if (pCtx == NULL)
++        ctx = GSS_C_NO_CONTEXT;
++    else
++        ctx = *pCtx;
++
++    if (ctx != GSS_C_NO_CONTEXT)
++        GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++#if 0
++    for (i = 0; i < sizeof(setCtxOps) / sizeof(setCtxOps[0]); i++) {
++        if (oidEqual(&setCtxOps[i].oid, desired_object)) {
++            major = (*setCtxOps[i].setOption)(minor, &ctx,
++                                              desired_object, value);
++            break;
++        }
++    }
++#endif
++
++    if (pCtx != NULL && *pCtx == NULL)
++        *pCtx = ctx;
++    else if (ctx != GSS_C_NO_CONTEXT)
++        GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/store_cred.c b/mech_eap/store_cred.c
+new file mode 100644
+index 0000000..d17a3ac
+--- /dev/null
++++ b/mech_eap/store_cred.c
+@@ -0,0 +1,83 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_store_cred(OM_uint32 *minor,
++               const gss_cred_id_t cred,
++               gss_cred_usage_t input_usage,
++               const gss_OID desired_mech GSSEAP_UNUSED,
++#ifdef GSSEAP_ENABLE_REAUTH
++               OM_uint32 overwrite_cred,
++               OM_uint32 default_cred,
++#else
++               OM_uint32 overwrite_cred GSSEAP_UNUSED,
++               OM_uint32 default_cred GSSEAP_UNUSED,
++#endif
++               gss_OID_set *elements_stored,
++               gss_cred_usage_t *cred_usage_stored)
++{
++    OM_uint32 major;
++
++    if (elements_stored != NULL)
++        *elements_stored = GSS_C_NO_OID_SET;
++    if (cred_usage_stored != NULL)
++        *cred_usage_stored = input_usage;
++
++    if (cred == GSS_C_NO_CREDENTIAL) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED;
++    }
++
++    GSSEAP_MUTEX_LOCK(&cred->mutex);
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++#ifdef GSSEAP_ENABLE_REAUTH
++    if (cred->reauthCred != GSS_C_NO_CREDENTIAL) {
++        major = gssStoreCred(minor,
++                             cred->reauthCred,
++                             input_usage,
++                             (gss_OID)gss_mech_krb5,
++                             overwrite_cred,
++                             default_cred,
++                             elements_stored,
++                             cred_usage_stored);
++    }
++#endif
++
++    GSSEAP_MUTEX_UNLOCK(&cred->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/unwrap.c b/mech_eap/unwrap.c
+new file mode 100644
+index 0000000..a185035
+--- /dev/null
++++ b/mech_eap/unwrap.c
+@@ -0,0 +1,85 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Message protection services: unwrap.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_unwrap(OM_uint32 *minor,
++           gss_ctx_id_t ctx,
++           gss_buffer_t input_message_buffer,
++           gss_buffer_t output_message_buffer,
++           int *conf_state,
++           gss_qop_t *qop_state)
++{
++    OM_uint32 major, tmpMinor;
++    gss_iov_buffer_desc iov[2];
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
++    iov[0].buffer = *input_message_buffer;
++
++    iov[1].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
++    iov[1].buffer.value = NULL;
++    iov[1].buffer.length = 0;
++
++    major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
++                                    iov, 2, TOK_TYPE_WRAP);
++    if (major == GSS_S_COMPLETE) {
++        *output_message_buffer = iov[1].buffer;
++    } else {
++        if (iov[1].type & GSS_IOV_BUFFER_FLAG_ALLOCATED)
++            gss_release_buffer(&tmpMinor, &iov[1].buffer);
++    }
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/unwrap_iov.c b/mech_eap/unwrap_iov.c
+new file mode 100644
+index 0000000..5ceefa2
+--- /dev/null
++++ b/mech_eap/unwrap_iov.c
+@@ -0,0 +1,572 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 2008 by the Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ */
++
++/*
++ * Message protection services: unwrap with scatter-gather API.
++ */
++
++#include "gssapiP_eap.h"
++
++/*
++ * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
++ * for DCE in which case it can just provide TOKEN | DATA (must
++ * guarantee that DATA is padded)
++ */
++OM_uint32
++unwrapToken(OM_uint32 *minor,
++            gss_ctx_id_t ctx,
++#ifdef HAVE_HEIMDAL_VERSION
++            krb5_crypto krbCrypto,
++#else
++            krb5_keyblock *unused GSSEAP_UNUSED,
++#endif
++            int *conf_state,
++            gss_qop_t *qop_state,
++            gss_iov_buffer_desc *iov,
++            int iov_count,
++            enum gss_eap_token_type toktype)
++{
++    OM_uint32 major = GSS_S_FAILURE, code;
++    gss_iov_buffer_t header;
++    gss_iov_buffer_t padding;
++    gss_iov_buffer_t trailer;
++    unsigned char flags;
++    unsigned char *ptr = NULL;
++    int keyUsage;
++    size_t rrc, ec;
++    size_t dataLen, assocDataLen;
++    uint64_t seqnum;
++    int valid = 0;
++    int conf_flag = 0;
++    krb5_context krbContext;
++#ifdef HAVE_HEIMDAL_VERSION
++    int freeCrypto = (krbCrypto == NULL);
++#endif
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    *minor = 0;
++
++    if (qop_state != NULL)
++        *qop_state = GSS_C_QOP_DEFAULT;
++
++    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
++    GSSEAP_ASSERT(header != NULL);
++
++    padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
++    if (padding != NULL && padding->buffer.length != 0) {
++        code = GSSEAP_BAD_PADDING_IOV;
++        major = GSS_S_DEFECTIVE_TOKEN;
++        goto cleanup;
++    }
++
++    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
++
++    flags = rfc4121Flags(ctx, TRUE);
++
++    if (toktype == TOK_TYPE_WRAP) {
++        keyUsage = !CTX_IS_INITIATOR(ctx)
++                   ? KEY_USAGE_INITIATOR_SEAL
++                   : KEY_USAGE_ACCEPTOR_SEAL;
++    } else {
++        keyUsage = !CTX_IS_INITIATOR(ctx)
++                   ? KEY_USAGE_INITIATOR_SIGN
++                   : KEY_USAGE_ACCEPTOR_SIGN;
++    }
++
++    gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
++
++    ptr = (unsigned char *)header->buffer.value;
++
++    if (header->buffer.length < 16) {
++        code = GSSEAP_TOK_TRUNC;
++        major = GSS_S_DEFECTIVE_TOKEN;
++        goto cleanup;
++    }
++
++    if ((ptr[2] & flags) != flags) {
++        code = GSSEAP_BAD_DIRECTION;
++        major = GSS_S_BAD_SIG;
++        goto cleanup;
++    }
++
++#ifdef HAVE_HEIMDAL_VERSION
++    if (krbCrypto == NULL) {
++        code = krb5_crypto_init(krbContext, &ctx->rfc3961Key,
++                                ETYPE_NULL, &krbCrypto);
++        if (code != 0)
++            goto cleanup;
++    }
++#endif
++
++    if (toktype == TOK_TYPE_WRAP) {
++        size_t krbTrailerLen;
++
++        if (load_uint16_be(ptr) != TOK_TYPE_WRAP)
++            goto defective;
++        conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
++        if (ptr[3] != 0xFF)
++            goto defective;
++        ec = load_uint16_be(ptr + 4);
++        rrc = load_uint16_be(ptr + 6);
++        seqnum = load_uint64_be(ptr + 8);
++
++        code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                               conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
++                                           KRB5_CRYPTO_TYPE_CHECKSUM,
++                               &krbTrailerLen);
++        if (code != 0)
++            goto cleanup;
++
++        /* Deal with RRC */
++        if (trailer == NULL) {
++            size_t desired_rrc = krbTrailerLen;
++
++            if (conf_flag) {
++                desired_rrc += 16; /* E(Header) */
++
++                if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0)
++                    desired_rrc += ec;
++            }
++
++            /* According to MS, we only need to deal with a fixed RRC for DCE */
++            if (rrc != desired_rrc)
++                goto defective;
++        } else if (rrc != 0) {
++            goto defective;
++        }
++
++        if (conf_flag) {
++            unsigned char *althdr;
++
++            /* Decrypt */
++            code = gssEapDecrypt(krbContext,
++                                 ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
++                                 ec, rrc, KRB_CRYPTO_CONTEXT(ctx), keyUsage,
++                                 iov, iov_count);
++            if (code != 0) {
++                major = GSS_S_BAD_SIG;
++                goto cleanup;
++            }
++
++            /* Validate header integrity */
++            if (trailer == NULL)
++                althdr = (unsigned char *)header->buffer.value + 16 + ec;
++            else
++                althdr = (unsigned char *)trailer->buffer.value + ec;
++
++            if (load_uint16_be(althdr) != TOK_TYPE_WRAP
++                || althdr[2] != ptr[2]
++                || althdr[3] != ptr[3]
++                || memcmp(althdr + 8, ptr + 8, 8) != 0) {
++                code = GSSEAP_BAD_WRAP_TOKEN;
++                major = GSS_S_BAD_SIG;
++                goto cleanup;
++            }
++        } else {
++            /* Verify checksum: note EC is checksum size here, not padding */
++            if (ec != krbTrailerLen)
++                goto defective;
++
++            /* Zero EC, RRC before computing checksum */
++            store_uint16_be(0, ptr + 4);
++            store_uint16_be(0, ptr + 6);
++
++            code = gssEapVerify(krbContext, ctx->checksumType, rrc,
++                                KRB_CRYPTO_CONTEXT(ctx), keyUsage,
++                                iov, iov_count, &valid);
++            if (code != 0 || valid == FALSE) {
++                major = GSS_S_BAD_SIG;
++                goto cleanup;
++            }
++        }
++
++        code = sequenceCheck(minor, &ctx->seqState, seqnum);
++    } else if (toktype == TOK_TYPE_MIC) {
++        if (load_uint16_be(ptr) != toktype)
++            goto defective;
++
++    verify_mic_1:
++        if (ptr[3] != 0xFF)
++            goto defective;
++        seqnum = load_uint64_be(ptr + 8);
++
++        /*
++         * Although MIC tokens don't have a RRC, they are similarly
++         * composed of a header and a checksum. So the verify_mic()
++         * can be implemented with a single header buffer, fake the
++         * RRC to the putative trailer length if no trailer buffer.
++         */
++        code = gssEapVerify(krbContext, ctx->checksumType,
++                            trailer != NULL ? 0 : header->buffer.length - 16,
++                            KRB_CRYPTO_CONTEXT(ctx), keyUsage,
++                            iov, iov_count, &valid);
++        if (code != 0 || valid == FALSE) {
++            major = GSS_S_BAD_SIG;
++            goto cleanup;
++        }
++        code = sequenceCheck(minor, &ctx->seqState, seqnum);
++    } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
++        if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
++            goto defective;
++        goto verify_mic_1;
++    } else {
++        goto defective;
++    }
++
++    if (conf_state != NULL)
++        *conf_state = conf_flag;
++
++    code = 0;
++    major = GSS_S_COMPLETE;
++    goto cleanup;
++
++defective:
++    code = GSSEAP_BAD_WRAP_TOKEN;
++    major = GSS_S_DEFECTIVE_TOKEN;
++
++cleanup:
++    *minor = code;
++#ifdef HAVE_HEIMDAL_VERSION
++    if (freeCrypto && krbCrypto != NULL)
++        krb5_crypto_destroy(krbContext, krbCrypto);
++#endif
++
++    return major;
++}
++
++int
++rotateLeft(void *ptr, size_t bufsiz, size_t rc)
++{
++    void *tbuf;
++
++    if (bufsiz == 0)
++        return 0;
++    rc = rc % bufsiz;
++    if (rc == 0)
++        return 0;
++
++    tbuf = GSSEAP_MALLOC(rc);
++    if (tbuf == NULL)
++        return ENOMEM;
++
++    memcpy(tbuf, ptr, rc);
++    memmove(ptr, (char *)ptr + rc, bufsiz - rc);
++    memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
++    GSSEAP_FREE(tbuf);
++
++    return 0;
++}
++
++/*
++ * Split a STREAM | SIGN_DATA | DATA into
++ *         HEADER | SIGN_DATA | DATA | PADDING | TRAILER
++ */
++static OM_uint32
++unwrapStream(OM_uint32 *minor,
++             gss_ctx_id_t ctx,
++             int *conf_state,
++             gss_qop_t *qop_state,
++             gss_iov_buffer_desc *iov,
++             int iov_count,
++             enum gss_eap_token_type toktype)
++{
++    unsigned char *ptr;
++    OM_uint32 code = 0, major = GSS_S_FAILURE;
++    krb5_context krbContext;
++    int conf_req_flag;
++    int i = 0, j;
++    gss_iov_buffer_desc *tiov = NULL;
++    gss_iov_buffer_t stream, data = NULL;
++    gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_crypto krbCrypto = NULL;
++#endif
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    GSSEAP_ASSERT(toktype == TOK_TYPE_WRAP);
++
++    if (toktype != TOK_TYPE_WRAP) {
++        code = GSSEAP_WRONG_TOK_ID;
++        goto cleanup;
++    }
++
++    stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
++    GSSEAP_ASSERT(stream != NULL);
++
++    if (stream->buffer.length < 16) {
++        major = GSS_S_DEFECTIVE_TOKEN;
++        goto cleanup;
++    }
++
++    ptr = (unsigned char *)stream->buffer.value;
++    ptr += 2; /* skip token type */
++
++    tiov = (gss_iov_buffer_desc *)GSSEAP_CALLOC((size_t)iov_count + 2,
++                                                sizeof(gss_iov_buffer_desc));
++    if (tiov == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++
++    /* HEADER */
++    theader = &tiov[i++];
++    theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
++    theader->buffer.value = stream->buffer.value;
++    theader->buffer.length = 16;
++
++    /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
++    for (j = 0; j < iov_count; j++) {
++        OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
++
++        if (type == GSS_IOV_BUFFER_TYPE_DATA) {
++            if (data != NULL) {
++                /* only a single DATA buffer can appear */
++                code = GSSEAP_BAD_STREAM_IOV;
++                goto cleanup;
++            }
++
++            data = &iov[j];
++            tdata = &tiov[i];
++        }
++        if (type == GSS_IOV_BUFFER_TYPE_DATA ||
++            type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
++            tiov[i++] = iov[j];
++    }
++
++    if (data == NULL) {
++        /* a single DATA buffer must be present */
++        code = GSSEAP_BAD_STREAM_IOV;
++        goto cleanup;
++    }
++
++    /* PADDING | TRAILER */
++    tpadding = &tiov[i++];
++    tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
++    tpadding->buffer.length = 0;
++    tpadding->buffer.value = NULL;
++
++    ttrailer = &tiov[i++];
++    ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
++    if (code != 0)
++        goto cleanup;
++#endif
++
++    {
++        size_t ec, rrc;
++        size_t krbHeaderLen = 0;
++        size_t krbTrailerLen = 0;
++
++        conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
++        ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0;
++        rrc = load_uint16_be(ptr + 4);
++
++        if (rrc != 0) {
++            code = rotateLeft((unsigned char *)stream->buffer.value + 16,
++                              stream->buffer.length - 16, rrc);
++            if (code != 0)
++                goto cleanup;
++            store_uint16_be(0, ptr + 4); /* set RRC to zero */
++        }
++
++        if (conf_req_flag) {
++            code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                                    KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
++            if (code != 0)
++                goto cleanup;
++            theader->buffer.length += krbHeaderLen; /* length validated later */
++        }
++
++        /* no PADDING for CFX, EC is used instead */
++        code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                               conf_req_flag
++                                  ? KRB5_CRYPTO_TYPE_TRAILER
++                                  : KRB5_CRYPTO_TYPE_CHECKSUM,
++                               &krbTrailerLen);
++        if (code != 0)
++            goto cleanup;
++
++        ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) +
++                                  krbTrailerLen;
++        ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
++            stream->buffer.length - ttrailer->buffer.length;
++    }
++
++    /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
++    /* CFX: GSS-Header | Kerb-Header | Data  |     | EC | E(Header) | Kerb-Trailer */
++    /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
++
++    /* validate lengths */
++    if (stream->buffer.length < theader->buffer.length +
++        tpadding->buffer.length +
++        ttrailer->buffer.length) {
++        major = GSS_S_DEFECTIVE_TOKEN;
++        code = GSSEAP_TOK_TRUNC;
++        goto cleanup;
++    }
++
++    /* setup data */
++    tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
++        tpadding->buffer.length - theader->buffer.length;
++
++    GSSEAP_ASSERT(data != NULL);
++
++    if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
++        code = gssEapAllocIov(tdata, tdata->buffer.length);
++        if (code != 0)
++            goto cleanup;
++
++        memcpy(tdata->buffer.value,
++               (unsigned char *)stream->buffer.value + theader->buffer.length,
++               tdata->buffer.length);
++    } else {
++        tdata->buffer.value = (unsigned char *)stream->buffer.value +
++                              theader->buffer.length;
++    }
++
++    GSSEAP_ASSERT(i <= iov_count + 2);
++
++    major = unwrapToken(&code, ctx, KRB_CRYPTO_CONTEXT(ctx),
++                        conf_state, qop_state, tiov, i, toktype);
++    if (major == GSS_S_COMPLETE) {
++        *data = *tdata;
++    } else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
++        OM_uint32 tmp;
++
++        gss_release_buffer(&tmp, &tdata->buffer);
++        tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
++    }
++
++cleanup:
++    if (tiov != NULL)
++        GSSEAP_FREE(tiov);
++#ifdef HAVE_HEIMDAL_VERSION
++    if (krbCrypto != NULL)
++        krb5_crypto_destroy(krbContext, krbCrypto);
++#endif
++
++    *minor = code;
++
++    return major;
++}
++
++OM_uint32
++gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
++                        gss_ctx_id_t ctx,
++                        int *conf_state,
++                        gss_qop_t *qop_state,
++                        gss_iov_buffer_desc *iov,
++                        int iov_count,
++                        enum gss_eap_token_type toktype)
++{
++    OM_uint32 major;
++
++    if (ctx->encryptionType == ENCTYPE_NULL) {
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
++        major = unwrapStream(minor, ctx, conf_state, qop_state,
++                             iov, iov_count, toktype);
++    } else {
++        major = unwrapToken(minor, ctx,
++                            NULL, /* krbCrypto */
++                            conf_state, qop_state,
++                            iov, iov_count, toktype);
++    }
++
++    return major;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_unwrap_iov(OM_uint32 *minor,
++               gss_ctx_id_t ctx,
++               int *conf_state,
++               gss_qop_t *qop_state,
++               gss_iov_buffer_desc *iov,
++               int iov_count)
++{
++    OM_uint32 major;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
++                                    iov, iov_count, TOK_TYPE_WRAP);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/util.h b/mech_eap/util.h
+new file mode 100644
+index 0000000..4f54d41
+--- /dev/null
++++ b/mech_eap/util.h
+@@ -0,0 +1,1032 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Portions Copyright 2003-2010 Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ *
++ */
++
++/*
++ * Utility functions.
++ */
++
++#ifndef _UTIL_H_
++#define _UTIL_H_ 1
++
++#ifdef HAVE_SYS_PARAM_H
++#include <sys/param.h>
++#endif
++#ifdef HAVE_STDINT_H
++#include <stdint.h>
++#endif
++#include <string.h>
++#include <errno.h>
++
++#include <krb5.h>
++
++#ifdef WIN32
++#define inline __inline
++#define snprintf _snprintf
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifndef MIN
++#define MIN(_a,_b)  ((_a)<(_b)?(_a):(_b))
++#endif
++
++#if !defined(WIN32) && !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
++#define GSSEAP_UNUSED __attribute__ ((__unused__))
++#else
++#define GSSEAP_UNUSED
++#endif
++
++/* util_buffer.c */
++OM_uint32
++makeStringBuffer(OM_uint32 *minor,
++                 const char *string,
++                 gss_buffer_t buffer);
++
++#define makeStringBufferOrCleanup(src, dst)             \
++    do {                                                \
++        major = makeStringBuffer((minor), (src), (dst));\
++        if (GSS_ERROR(major))                           \
++            goto cleanup;                               \
++    } while (0)
++
++OM_uint32
++bufferToString(OM_uint32 *minor,
++               const gss_buffer_t buffer,
++               char **pString);
++
++OM_uint32
++duplicateBuffer(OM_uint32 *minor,
++                const gss_buffer_t src,
++                gss_buffer_t dst);
++
++#define duplicateBufferOrCleanup(src, dst)              \
++    do {                                                \
++        major = duplicateBuffer((minor), (src), (dst)); \
++        if (GSS_ERROR(major))                           \
++            goto cleanup;                               \
++    } while (0)
++
++static inline int
++bufferEqual(const gss_buffer_t b1, const gss_buffer_t b2)
++{
++    return (b1->length == b2->length &&
++            memcmp(b1->value, b2->value, b2->length) == 0);
++}
++
++static inline int
++bufferEqualString(const gss_buffer_t b1, const char *s)
++{
++    gss_buffer_desc b2;
++
++    b2.length = strlen(s);
++    b2.value = (char *)s;
++
++    return bufferEqual(b1, &b2);
++}
++
++/* util_cksum.c */
++int
++gssEapSign(krb5_context context,
++           krb5_cksumtype type,
++           size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++           krb5_crypto crypto,
++#else
++           krb5_keyblock *key,
++#endif
++           krb5_keyusage sign_usage,
++           gss_iov_buffer_desc *iov,
++           int iov_count);
++
++int
++gssEapVerify(krb5_context context,
++             krb5_cksumtype type,
++             size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++             krb5_crypto crypto,
++#else
++             krb5_keyblock *key,
++#endif
++             krb5_keyusage sign_usage,
++             gss_iov_buffer_desc *iov,
++             int iov_count,
++             int *valid);
++
++#if 0
++OM_uint32
++gssEapEncodeGssChannelBindings(OM_uint32 *minor,
++                               gss_channel_bindings_t chanBindings,
++                               gss_buffer_t encodedBindings);
++#endif
++
++/* util_context.c */
++#define EAP_EXPORT_CONTEXT_V1           1
++
++enum gss_eap_token_type {
++    TOK_TYPE_NONE                    = 0x0000,  /* no token */
++    TOK_TYPE_MIC                     = 0x0404,  /* RFC 4121 MIC token */
++    TOK_TYPE_WRAP                    = 0x0504,  /* RFC 4121 wrap token */
++    TOK_TYPE_EXPORT_NAME             = 0x0401,  /* RFC 2743 exported name */
++    TOK_TYPE_EXPORT_NAME_COMPOSITE   = 0x0402,  /* exported composite name */
++    TOK_TYPE_DELETE_CONTEXT          = 0x0405,  /* RFC 2743 delete context */
++    TOK_TYPE_INITIATOR_CONTEXT       = 0x0601,  /* initiator-sent context token */
++    TOK_TYPE_ACCEPTOR_CONTEXT        = 0x0602,  /* acceptor-sent context token */
++};
++
++/* inner token types and flags */
++#define ITOK_TYPE_NONE                  0x00000000
++#define ITOK_TYPE_CONTEXT_ERR           0x00000001 /* critical */
++#define ITOK_TYPE_ACCEPTOR_NAME_REQ     0x00000002 /* TBD */
++#define ITOK_TYPE_ACCEPTOR_NAME_RESP    0x00000003 /* TBD */
++#define ITOK_TYPE_EAP_RESP              0x00000004 /* critical, required, if not reauth */
++#define ITOK_TYPE_EAP_REQ               0x00000005 /* critical, required, if not reauth */
++#define ITOK_TYPE_GSS_CHANNEL_BINDINGS  0x00000006 /* critical, required, if not reauth */
++#define ITOK_TYPE_REAUTH_CREDS          0x00000007 /* optional */
++#define ITOK_TYPE_REAUTH_REQ            0x00000008 /* optional */
++#define ITOK_TYPE_REAUTH_RESP           0x00000009 /* optional */
++#define ITOK_TYPE_VERSION_INFO          0x0000000A /* optional */
++#define ITOK_TYPE_VENDOR_INFO           0x0000000B /* optional */
++#define ITOK_TYPE_GSS_FLAGS             0x0000000C /* optional */
++#define ITOK_TYPE_INITIATOR_MIC         0x0000000D /* critical, required, if not reauth */
++#define ITOK_TYPE_ACCEPTOR_MIC          0x0000000E /* TBD */
++
++#define ITOK_FLAG_CRITICAL              0x80000000  /* critical, wire flag */
++#define ITOK_FLAG_VERIFIED              0x40000000  /* verified, API flag */
++
++#define ITOK_TYPE_MASK                  (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED))
++
++#define GSSEAP_WIRE_FLAGS_MASK          ( GSS_C_MUTUAL_FLAG             | \
++                                          GSS_C_DCE_STYLE               | \
++                                          GSS_C_IDENTIFY_FLAG           | \
++                                          GSS_C_EXTENDED_ERROR_FLAG       )
++
++OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
++OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
++
++OM_uint32
++gssEapMakeToken(OM_uint32 *minor,
++                gss_ctx_id_t ctx,
++                const gss_buffer_t innerToken,
++                enum gss_eap_token_type tokenType,
++                gss_buffer_t outputToken);
++
++OM_uint32
++gssEapVerifyToken(OM_uint32 *minor,
++                  gss_ctx_id_t ctx,
++                  const gss_buffer_t inputToken,
++                  enum gss_eap_token_type *tokenType,
++                  gss_buffer_t innerInputToken);
++
++OM_uint32
++gssEapContextTime(OM_uint32 *minor,
++                  gss_ctx_id_t context_handle,
++                  OM_uint32 *time_rec);
++
++OM_uint32
++gssEapMakeTokenMIC(OM_uint32 *minor,
++                   gss_ctx_id_t ctx,
++                   gss_buffer_t tokenMIC);
++
++OM_uint32
++gssEapVerifyTokenMIC(OM_uint32 *minor,
++                     gss_ctx_id_t ctx,
++                     const gss_buffer_t tokenMIC);
++
++/* util_cred.c */
++OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred);
++OM_uint32 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred);
++
++gss_OID
++gssEapPrimaryMechForCred(gss_cred_id_t cred);
++
++OM_uint32
++gssEapAcquireCred(OM_uint32 *minor,
++                  const gss_name_t desiredName,
++                  OM_uint32 timeReq,
++                  const gss_OID_set desiredMechs,
++                  int cred_usage,
++                  gss_cred_id_t *pCred,
++                  gss_OID_set *pActualMechs,
++                  OM_uint32 *timeRec);
++
++OM_uint32
++gssEapSetCredPassword(OM_uint32 *minor,
++                      gss_cred_id_t cred,
++                      const gss_buffer_t password);
++
++OM_uint32
++gssEapSetCredService(OM_uint32 *minor,
++                     gss_cred_id_t cred,
++                     const gss_name_t target);
++
++OM_uint32
++gssEapResolveInitiatorCred(OM_uint32 *minor,
++                           const gss_cred_id_t cred,
++                           const gss_name_t target,
++                           gss_cred_id_t *resolvedCred);
++
++int gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech);
++
++OM_uint32
++gssEapInquireCred(OM_uint32 *minor,
++                  gss_cred_id_t cred,
++                  gss_name_t *name,
++                  OM_uint32 *pLifetime,
++                  gss_cred_usage_t *cred_usage,
++                  gss_OID_set *mechanisms);
++
++/* util_crypt.c */
++int
++gssEapEncrypt(krb5_context context, int dce_style, size_t ec,
++              size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++              krb5_crypto crypto,
++#else
++              krb5_keyblock *key,
++#endif
++              int usage,
++              gss_iov_buffer_desc *iov, int iov_count);
++
++int
++gssEapDecrypt(krb5_context context, int dce_style, size_t ec,
++              size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++              krb5_crypto crypto,
++#else
++              krb5_keyblock *key,
++#endif
++              int usage,
++              gss_iov_buffer_desc *iov, int iov_count);
++
++int
++gssEapMapCryptoFlag(OM_uint32 type);
++
++gss_iov_buffer_t
++gssEapLocateIov(gss_iov_buffer_desc *iov,
++                int iov_count,
++                OM_uint32 type);
++
++void
++gssEapIovMessageLength(gss_iov_buffer_desc *iov,
++                       int iov_count,
++                       size_t *data_length,
++                       size_t *assoc_data_length);
++
++void
++gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count);
++
++int
++gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count);
++
++int
++gssEapAllocIov(gss_iov_buffer_t iov, size_t size);
++
++OM_uint32
++gssEapDeriveRfc3961Key(OM_uint32 *minor,
++                       const unsigned char *key,
++                       size_t keyLength,
++                       krb5_enctype enctype,
++                       krb5_keyblock *pKey);
++
++/* util_krb.c */
++
++#ifndef KRB_MALLOC
++/*
++ * If your Kerberos library uses a different allocator to your
++ * GSS mechanism glue, then you might wish to define these in
++ * config.h or elsewhere. This should eventually go away when
++ * we no longer need to allocate memory that is freed by the
++ * Kerberos library.
++ */
++#define KRB_CALLOC                      calloc
++#define KRB_MALLOC                      malloc
++#define KRB_FREE                        free
++#define KRB_REALLOC                     realloc
++#endif /* KRB_MALLOC */
++
++#ifdef HAVE_HEIMDAL_VERSION
++
++#define KRB_TIME_FOREVER        ((time_t)~0L)
++
++#define KRB_KEY_TYPE(key)       ((key)->keytype)
++#define KRB_KEY_DATA(key)       ((key)->keyvalue.data)
++#define KRB_KEY_LENGTH(key)     ((key)->keyvalue.length)
++
++#define KRB_PRINC_LENGTH(princ) ((princ)->name.name_string.len)
++#define KRB_PRINC_TYPE(princ)   ((princ)->name.name_type)
++#define KRB_PRINC_NAME(princ)   ((princ)->name.name_string.val)
++#define KRB_PRINC_REALM(princ)  ((princ)->realm)
++
++#define KRB_KT_ENT_KEYBLOCK(e)  (&(e)->keyblock)
++#define KRB_KT_ENT_FREE(c, e)   krb5_kt_free_entry((c), (e))
++
++#define KRB_CRYPTO_CONTEXT(ctx) (krbCrypto)
++
++#define KRB_DATA_INIT(d)        krb5_data_zero((d))
++
++#else
++
++#define KRB_TIME_FOREVER        KRB5_INT32_MAX
++
++#define KRB_KEY_TYPE(key)       ((key)->enctype)
++#define KRB_KEY_DATA(key)       ((key)->contents)
++#define KRB_KEY_LENGTH(key)     ((key)->length)
++
++#define KRB_PRINC_LENGTH(princ) (krb5_princ_size(NULL, (princ)))
++#define KRB_PRINC_TYPE(princ)   (krb5_princ_type(NULL, (princ)))
++#define KRB_PRINC_NAME(princ)   (krb5_princ_name(NULL, (princ)))
++#define KRB_PRINC_REALM(princ)  (krb5_princ_realm(NULL, (princ)))
++
++#define KRB_KT_ENT_KEYBLOCK(e)  (&(e)->key)
++#define KRB_KT_ENT_FREE(c, e)   krb5_free_keytab_entry_contents((c), (e))
++
++#define KRB_CRYPTO_CONTEXT(ctx) (&(ctx)->rfc3961Key)
++
++#define KRB_DATA_INIT(d)        do {        \
++        (d)->magic = KV5M_DATA;             \
++        (d)->length = 0;                    \
++        (d)->data = NULL;                   \
++    } while (0)
++
++#endif /* HAVE_HEIMDAL_VERSION */
++
++#define KRB_KEY_INIT(key)       do {        \
++        KRB_KEY_TYPE(key) = ENCTYPE_NULL;   \
++        KRB_KEY_DATA(key) = NULL;           \
++        KRB_KEY_LENGTH(key) = 0;            \
++    } while (0)
++
++#define GSSEAP_KRB_INIT(ctx) do {                   \
++        OM_uint32 tmpMajor;                         \
++                                                    \
++        tmpMajor  = gssEapKerberosInit(minor, ctx); \
++        if (GSS_ERROR(tmpMajor)) {                  \
++            return tmpMajor;                        \
++        }                                           \
++    } while (0)
++
++OM_uint32
++gssEapKerberosInit(OM_uint32 *minor, krb5_context *context);
++
++OM_uint32
++rfc3961ChecksumTypeForKey(OM_uint32 *minor,
++                          krb5_keyblock *key,
++                          krb5_cksumtype *cksumtype);
++
++krb5_error_code
++krbCryptoLength(krb5_context krbContext,
++#ifdef HAVE_HEIMDAL_VERSION
++                krb5_crypto krbCrypto,
++#else
++                krb5_keyblock *key,
++#endif
++                int type,
++                size_t *length);
++
++krb5_error_code
++krbPaddingLength(krb5_context krbContext,
++#ifdef HAVE_HEIMDAL_VERSION
++                 krb5_crypto krbCrypto,
++#else
++                 krb5_keyblock *key,
++#endif
++                 size_t dataLength,
++                 size_t *padLength);
++
++krb5_error_code
++krbBlockSize(krb5_context krbContext,
++#ifdef HAVE_HEIMDAL_VERSION
++                 krb5_crypto krbCrypto,
++#else
++                 krb5_keyblock *key,
++#endif
++                 size_t *blockSize);
++
++krb5_error_code
++krbEnctypeToString(krb5_context krbContext,
++                   krb5_enctype enctype,
++                   const char *prefix,
++                   gss_buffer_t string);
++
++krb5_error_code
++krbMakeAuthDataKdcIssued(krb5_context context,
++                         const krb5_keyblock *key,
++                         krb5_const_principal issuer,
++#ifdef HAVE_HEIMDAL_VERSION
++                         const AuthorizationData *authdata,
++                         AuthorizationData *adKdcIssued
++#else
++                         krb5_authdata *const *authdata,
++                         krb5_authdata ***adKdcIssued
++#endif
++                         );
++
++krb5_error_code
++krbMakeCred(krb5_context context,
++            krb5_auth_context authcontext,
++            krb5_creds *creds,
++            krb5_data *data);
++
++/* util_lucid.c */
++OM_uint32
++gssEapExportLucidSecContext(OM_uint32 *minor,
++                            gss_ctx_id_t ctx,
++                            const gss_OID desiredObject,
++                            gss_buffer_set_t *data_set);
++
++/* util_mech.c */
++extern gss_OID GSS_EAP_MECHANISM;
++
++#define OID_FLAG_NULL_VALID                 0x00000001
++#define OID_FLAG_FAMILY_MECH_VALID          0x00000002
++#define OID_FLAG_MAP_NULL_TO_DEFAULT_MECH   0x00000004
++#define OID_FLAG_MAP_FAMILY_MECH_TO_NULL    0x00000008
++
++OM_uint32
++gssEapCanonicalizeOid(OM_uint32 *minor,
++                      const gss_OID oid,
++                      OM_uint32 flags,
++                      gss_OID *pOid);
++
++OM_uint32
++gssEapReleaseOid(OM_uint32 *minor, gss_OID *oid);
++
++OM_uint32
++gssEapDefaultMech(OM_uint32 *minor,
++                  gss_OID *oid);
++
++OM_uint32
++gssEapIndicateMechs(OM_uint32 *minor,
++                    gss_OID_set *mechs);
++
++OM_uint32
++gssEapEnctypeToOid(OM_uint32 *minor,
++                   krb5_enctype enctype,
++                   gss_OID *pOid);
++
++OM_uint32
++gssEapOidToEnctype(OM_uint32 *minor,
++                   const gss_OID oid,
++                   krb5_enctype *enctype);
++
++int
++gssEapIsMechanismOid(const gss_OID oid);
++
++int
++gssEapIsConcreteMechanismOid(const gss_OID oid);
++
++OM_uint32
++gssEapValidateMechs(OM_uint32 *minor,
++                   const gss_OID_set mechs);
++
++gss_buffer_t
++gssEapOidToSaslName(const gss_OID oid);
++
++gss_OID
++gssEapSaslNameToOid(const gss_buffer_t name);
++
++/* util_moonshot.c */
++OM_uint32
++libMoonshotResolveDefaultIdentity(OM_uint32 *minor,
++                                  const gss_cred_id_t cred,
++                                  gss_name_t *pName);
++
++OM_uint32
++libMoonshotResolveInitiatorCred(OM_uint32 *minor,
++                                gss_cred_id_t cred,
++                                const gss_name_t targetName);
++
++/* util_name.c */
++#define EXPORT_NAME_FLAG_OID                    0x1
++#define EXPORT_NAME_FLAG_COMPOSITE              0x2
++#define EXPORT_NAME_FLAG_ALLOW_COMPOSITE        0x4
++
++OM_uint32 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName);
++OM_uint32 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName);
++OM_uint32 gssEapExportName(OM_uint32 *minor,
++                           const gss_name_t name,
++                           gss_buffer_t exportedName);
++OM_uint32 gssEapExportNameInternal(OM_uint32 *minor,
++                                   const gss_name_t name,
++                                   gss_buffer_t exportedName,
++                                   OM_uint32 flags);
++OM_uint32 gssEapImportName(OM_uint32 *minor,
++                           const gss_buffer_t input_name_buffer,
++                           const gss_OID input_name_type,
++                           const gss_OID input_mech_type,
++                           gss_name_t *output_name);
++OM_uint32 gssEapImportNameInternal(OM_uint32 *minor,
++                                   const gss_buffer_t input_name_buffer,
++                                   gss_name_t *output_name,
++                                   OM_uint32 flags);
++OM_uint32
++gssEapDuplicateName(OM_uint32 *minor,
++                    const gss_name_t input_name,
++                    gss_name_t *dest_name);
++
++OM_uint32
++gssEapCanonicalizeName(OM_uint32 *minor,
++                       const gss_name_t input_name,
++                       const gss_OID mech_type,
++                       gss_name_t *dest_name);
++
++OM_uint32
++gssEapDisplayName(OM_uint32 *minor,
++                  gss_name_t name,
++                  gss_buffer_t output_name_buffer,
++                  gss_OID *output_name_type);
++
++OM_uint32
++gssEapCompareName(OM_uint32 *minor,
++                  gss_name_t name1,
++                  gss_name_t name2,
++                  int *name_equal);
++
++/* util_oid.c */
++OM_uint32
++composeOid(OM_uint32 *minor_status,
++           const char *prefix,
++           size_t prefix_len,
++           int suffix,
++           gss_OID_desc *oid);
++
++OM_uint32
++decomposeOid(OM_uint32 *minor_status,
++             const char *prefix,
++             size_t prefix_len,
++             gss_OID_desc *oid,
++             int *suffix) ;
++
++OM_uint32
++duplicateOid(OM_uint32 *minor_status,
++             const gss_OID_desc * const oid,
++             gss_OID *new_oid);
++
++OM_uint32
++duplicateOidSet(OM_uint32 *minor,
++                const gss_OID_set src,
++                gss_OID_set *dst);
++
++static inline int
++oidEqual(const gss_OID_desc *o1, const gss_OID_desc *o2)
++{
++    if (o1 == GSS_C_NO_OID)
++        return (o2 == GSS_C_NO_OID);
++    else if (o2 == GSS_C_NO_OID)
++        return (o1 == GSS_C_NO_OID);
++    else
++        return (o1->length == o2->length &&
++                memcmp(o1->elements, o2->elements, o1->length) == 0);
++}
++
++/* util_ordering.c */
++OM_uint32
++sequenceInternalize(OM_uint32 *minor,
++                    void **vqueue,
++                    unsigned char **buf,
++                    size_t *lenremain);
++
++OM_uint32
++sequenceExternalize(OM_uint32 *minor,
++                    void *vqueue,
++                    unsigned char **buf,
++                    size_t *lenremain);
++
++size_t
++sequenceSize(void *vqueue);
++
++OM_uint32
++sequenceFree(OM_uint32 *minor, void **vqueue);
++
++OM_uint32
++sequenceCheck(OM_uint32 *minor, void **vqueue, uint64_t seqnum);
++
++OM_uint32
++sequenceInit(OM_uint32 *minor, void **vqueue, uint64_t seqnum,
++             int do_replay, int do_sequence, int wide_nums);
++
++/* util_sm.c */
++enum gss_eap_state {
++    GSSEAP_STATE_INITIAL        = 0x01,     /* initial state */
++    GSSEAP_STATE_AUTHENTICATE   = 0x02,     /* exchange EAP messages */
++    GSSEAP_STATE_INITIATOR_EXTS = 0x04,     /* initiator extensions */
++    GSSEAP_STATE_ACCEPTOR_EXTS  = 0x08,     /* acceptor extensions */
++#ifdef GSSEAP_ENABLE_REAUTH
++    GSSEAP_STATE_REAUTHENTICATE = 0x10,     /* GSS reauthentication messages */
++#endif
++    GSSEAP_STATE_ESTABLISHED    = 0x20,     /* context established */
++    GSSEAP_STATE_ALL            = 0x3F
++};
++
++#define GSSEAP_STATE_NEXT(s)    ((s) << 1)
++
++#define GSSEAP_SM_STATE(ctx)                ((ctx)->state)
++
++#ifdef GSSEAP_DEBUG
++void gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
++#define GSSEAP_SM_TRANSITION(ctx, state)    gssEapSmTransition((ctx), (state))
++#else
++#define GSSEAP_SM_TRANSITION(ctx, newstate)    do { (ctx)->state = (newstate); } while (0)
++#endif
++
++#define GSSEAP_SM_TRANSITION_NEXT(ctx)      GSSEAP_SM_TRANSITION((ctx), GSSEAP_STATE_NEXT(GSSEAP_SM_STATE((ctx))))
++
++/* state machine entry */
++struct gss_eap_sm {
++    OM_uint32 inputTokenType;
++    OM_uint32 outputTokenType;
++    enum gss_eap_state validStates;
++    OM_uint32 itokFlags;
++    OM_uint32 (*processToken)(OM_uint32 *,
++                              gss_cred_id_t,
++                              gss_ctx_id_t,
++                              gss_name_t,
++                              gss_OID,
++                              OM_uint32,
++                              OM_uint32,
++                              gss_channel_bindings_t,
++                              gss_buffer_t,
++                              gss_buffer_t,
++                              OM_uint32 *);
++};
++
++/* state machine flags, set by handler */
++#define SM_FLAG_FORCE_SEND_TOKEN            0x00000001  /* send token even if no inner tokens */
++#define SM_FLAG_OUTPUT_TOKEN_CRITICAL       0x00000002  /* output token is critical */
++
++/* state machine flags, set by state machine */
++#define SM_FLAG_INPUT_TOKEN_CRITICAL        0x10000000  /* input token was critical */
++
++#define SM_ITOK_FLAG_REQUIRED               0x00000001  /* received tokens must be present */
++
++OM_uint32
++gssEapSmStep(OM_uint32 *minor,
++             gss_cred_id_t cred,
++             gss_ctx_id_t ctx,
++             gss_name_t target,
++             gss_OID mech,
++             OM_uint32 reqFlags,
++             OM_uint32 timeReq,
++             gss_channel_bindings_t chanBindings,
++             gss_buffer_t inputToken,
++             gss_buffer_t outputToken,
++             struct gss_eap_sm *sm,
++             size_t smCount);
++
++void
++gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
++
++/* util_token.c */
++struct gss_eap_token_buffer_set {
++    gss_buffer_set_desc buffers; /* pointers only */
++    OM_uint32 *types;
++};
++
++OM_uint32
++gssEapEncodeInnerTokens(OM_uint32 *minor,
++                        struct gss_eap_token_buffer_set *tokens,
++                        gss_buffer_t buffer);
++OM_uint32
++gssEapDecodeInnerTokens(OM_uint32 *minor,
++                        const gss_buffer_t buffer,
++                        struct gss_eap_token_buffer_set *tokens);
++
++OM_uint32
++gssEapReleaseInnerTokens(OM_uint32 *minor,
++                         struct gss_eap_token_buffer_set *tokens,
++                         int freeBuffers);
++
++OM_uint32
++gssEapAllocInnerTokens(OM_uint32 *minor,
++                       size_t count,
++                       struct gss_eap_token_buffer_set *tokens);
++
++size_t
++tokenSize(const gss_OID_desc *mech, size_t body_size);
++
++void
++makeTokenHeader(const gss_OID_desc *mech,
++                size_t body_size,
++                unsigned char **buf,
++                enum gss_eap_token_type tok_type);
++
++OM_uint32
++verifyTokenHeader(OM_uint32 *minor,
++                  gss_OID mech,
++                  size_t *body_size,
++                  unsigned char **buf_in,
++                  size_t toksize_in,
++                  enum gss_eap_token_type *ret_tok_type);
++
++/* Helper macros */
++
++#ifndef GSSEAP_MALLOC
++#define GSSEAP_CALLOC                   calloc
++#define GSSEAP_MALLOC                   malloc
++#define GSSEAP_FREE                     free
++#define GSSEAP_REALLOC                  realloc
++#endif
++
++#ifndef GSSAPI_CALLCONV
++#define GSSAPI_CALLCONV                 KRB5_CALLCONV
++#endif
++
++#ifndef GSSEAP_ASSERT
++#include <assert.h>
++#define GSSEAP_ASSERT(x)                assert((x))
++#endif /* !GSSEAP_ASSERT */
++
++#ifdef WIN32
++#define GSSEAP_CONSTRUCTOR
++#define GSSEAP_DESTRUCTOR
++#else
++#define GSSEAP_CONSTRUCTOR              __attribute__((constructor))
++#define GSSEAP_DESTRUCTOR               __attribute__((destructor))
++#endif
++
++#define GSSEAP_NOT_IMPLEMENTED          do {            \
++        GSSEAP_ASSERT(0 && "not implemented");          \
++        *minor = ENOSYS;                                \
++        return GSS_S_FAILURE;                           \
++    } while (0)
++
++#ifdef WIN32
++
++#include <winbase.h>
++
++#define GSSEAP_GET_LAST_ERROR()         (GetLastError()) /* XXX FIXME */
++
++#define GSSEAP_MUTEX                    CRITICAL_SECTION
++#define GSSEAP_MUTEX_INIT(m)            (InitializeCriticalSection((m)), 0)
++#define GSSEAP_MUTEX_DESTROY(m)         DeleteCriticalSection((m))
++#define GSSEAP_MUTEX_LOCK(m)            EnterCriticalSection((m))
++#define GSSEAP_MUTEX_UNLOCK(m)          LeaveCriticalSection((m))
++#define GSSEAP_ONCE_LEAVE             do { return TRUE; } while (0)
++
++/* Thread-local is handled separately */
++
++#define GSSEAP_THREAD_ONCE              INIT_ONCE
++#define GSSEAP_ONCE_CALLBACK(cb)        BOOL CALLBACK cb(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
++#define GSSEAP_ONCE(o, i)               InitOnceExecuteOnce((o), (i), NULL, NULL)
++#define GSSEAP_ONCE_INITIALIZER         INIT_ONCE_STATIC_INIT
++
++#else
++
++#include <pthread.h>
++
++#define GSSEAP_GET_LAST_ERROR()         (errno)
++
++#define GSSEAP_MUTEX                    pthread_mutex_t
++#define GSSEAP_MUTEX_INIT(m)            pthread_mutex_init((m), NULL)
++#define GSSEAP_MUTEX_DESTROY(m)         pthread_mutex_destroy((m))
++#define GSSEAP_MUTEX_LOCK(m)            pthread_mutex_lock((m))
++#define GSSEAP_MUTEX_UNLOCK(m)          pthread_mutex_unlock((m))
++
++#define GSSEAP_THREAD_KEY               pthread_key_t
++#define GSSEAP_KEY_CREATE(k, d)         pthread_key_create((k), (d))
++#define GSSEAP_GETSPECIFIC(k)           pthread_getspecific((k))
++#define GSSEAP_SETSPECIFIC(k, d)        pthread_setspecific((k), (d))
++
++#define GSSEAP_THREAD_ONCE              pthread_once_t
++#define GSSEAP_ONCE_CALLBACK(cb)        void cb(void)
++#define GSSEAP_ONCE(o, i)               pthread_once((o), (i))
++#define GSSEAP_ONCE_INITIALIZER         PTHREAD_ONCE_INIT
++#define GSSEAP_ONCE_LEAVE             do { } while (0)
++
++#endif /* WIN32 */
++
++/* Helper functions */
++static inline void
++store_uint16_be(uint16_t val, void *vp)
++{
++    unsigned char *p = (unsigned char *)vp;
++
++    p[0] = (val >>  8) & 0xff;
++    p[1] = (val      ) & 0xff;
++}
++
++static inline uint16_t
++load_uint16_be(const void *cvp)
++{
++    const unsigned char *p = (const unsigned char *)cvp;
++
++    return (p[1] | (p[0] << 8));
++}
++
++static inline void
++store_uint32_be(uint32_t val, void *vp)
++{
++    unsigned char *p = (unsigned char *)vp;
++
++    p[0] = (val >> 24) & 0xff;
++    p[1] = (val >> 16) & 0xff;
++    p[2] = (val >>  8) & 0xff;
++    p[3] = (val      ) & 0xff;
++}
++
++static inline uint32_t
++load_uint32_be(const void *cvp)
++{
++    const unsigned char *p = (const unsigned char *)cvp;
++
++    return (p[3] | (p[2] << 8)
++            | ((uint32_t) p[1] << 16)
++            | ((uint32_t) p[0] << 24));
++}
++
++static inline void
++store_uint64_be(uint64_t val, void *vp)
++{
++    unsigned char *p = (unsigned char *)vp;
++
++    p[0] = (unsigned char)((val >> 56) & 0xff);
++    p[1] = (unsigned char)((val >> 48) & 0xff);
++    p[2] = (unsigned char)((val >> 40) & 0xff);
++    p[3] = (unsigned char)((val >> 32) & 0xff);
++    p[4] = (unsigned char)((val >> 24) & 0xff);
++    p[5] = (unsigned char)((val >> 16) & 0xff);
++    p[6] = (unsigned char)((val >>  8) & 0xff);
++    p[7] = (unsigned char)((val      ) & 0xff);
++}
++
++static inline uint64_t
++load_uint64_be(const void *cvp)
++{
++    const unsigned char *p = (const unsigned char *)cvp;
++
++    return ((uint64_t)load_uint32_be(p) << 32) | load_uint32_be(p + 4);
++}
++
++static inline unsigned char *
++store_buffer(gss_buffer_t buffer, void *vp, int wide_nums)
++{
++    unsigned char *p = (unsigned char *)vp;
++
++    if (wide_nums) {
++        store_uint64_be(buffer->length, p);
++        p += 8;
++    } else {
++        store_uint32_be(buffer->length, p);
++        p += 4;
++    }
++
++    if (buffer->value != NULL) {
++        memcpy(p, buffer->value, buffer->length);
++        p += buffer->length;
++    }
++
++    return p;
++}
++
++static inline unsigned char *
++load_buffer(const void *cvp, size_t length, gss_buffer_t buffer)
++{
++    buffer->length = 0;
++    buffer->value = GSSEAP_MALLOC(length);
++    if (buffer->value == NULL)
++        return NULL;
++    buffer->length = length;
++    memcpy(buffer->value, cvp, length);
++    return (unsigned char *)cvp + length;
++}
++
++static inline unsigned char *
++store_oid(gss_OID oid, void *vp)
++{
++    gss_buffer_desc buf;
++
++    if (oid != GSS_C_NO_OID) {
++        buf.length = oid->length;
++        buf.value = oid->elements;
++    } else {
++        buf.length = 0;
++        buf.value = NULL;
++    }
++
++    return store_buffer(&buf, vp, FALSE);
++}
++
++static inline void
++krbDataToGssBuffer(krb5_data *data, gss_buffer_t buffer)
++{
++    buffer->value = (void *)data->data;
++    buffer->length = data->length;
++}
++
++static inline void
++krbPrincComponentToGssBuffer(krb5_principal krbPrinc,
++                             int index, gss_buffer_t buffer)
++{
++#ifdef HAVE_HEIMDAL_VERSION
++    buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index];
++    buffer->length = strlen((char *)buffer->value);
++#else
++    buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data;
++    buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length;
++#endif /* HAVE_HEIMDAL_VERSION */
++}
++
++static inline void
++krbPrincRealmToGssBuffer(krb5_principal krbPrinc, gss_buffer_t buffer)
++{
++#ifdef HAVE_HEIMDAL_VERSION
++    buffer->value = (void *)KRB_PRINC_REALM(krbPrinc);
++    buffer->length = strlen((char *)buffer->value);
++#else
++    krbDataToGssBuffer(KRB_PRINC_REALM(krbPrinc), buffer);
++#endif
++}
++
++static inline void
++gssBufferToKrbData(gss_buffer_t buffer, krb5_data *data)
++{
++    data->data = (char *)buffer->value;
++    data->length = buffer->length;
++}
++
++/* util_tld.c */
++struct gss_eap_status_info;
++
++struct gss_eap_thread_local_data {
++    krb5_context krbContext;
++    struct gss_eap_status_info *statusInfo;
++};
++
++struct gss_eap_thread_local_data *
++gssEapGetThreadLocalData(void);
++
++void
++gssEapDestroyStatusInfo(struct gss_eap_status_info *status);
++
++void
++gssEapDestroyKrbContext(krb5_context context);
++
++#ifdef __cplusplus
++}
++#endif
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++#include "util_json.h"
++#include "util_attr.h"
++#include "util_base64.h"
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++#ifdef GSSEAP_ENABLE_REAUTH
++#include "util_reauth.h"
++#endif
++
++#endif /* _UTIL_H_ */
+diff --git a/mech_eap/util_adshim.c b/mech_eap/util_adshim.c
+new file mode 100644
+index 0000000..513a1a8
+--- /dev/null
++++ b/mech_eap/util_adshim.c
+@@ -0,0 +1,242 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++#include "authdata_plugin.h"
++
++/*
++ * This rubbish is necessary because MIT doesn't provide another way
++ * to access verified AD-KDCIssued elements. We can't verify them
++ * ourselves because they're signed in the ticket session key, which
++ * is destroyed immediately after the AP-REQ is processed.
++ */
++
++struct radius_ad_context {
++    krb5_data avpdata;
++    krb5_boolean verified;
++};
++
++static krb5_data radius_ad_attr = {
++    KV5M_DATA, sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" };
++
++static krb5_error_code
++radius_ad_init(krb5_context kcontext GSSEAP_UNUSED,
++               void **plugin_context)
++{
++    *plugin_context = 0;
++    return 0;
++}
++
++static void
++radius_ad_flags(krb5_context kcontext GSSEAP_UNUSED,
++                void *plugin_context GSSEAP_UNUSED,
++                krb5_authdatatype ad_type GSSEAP_UNUSED,
++                krb5_flags *flags)
++{
++    *flags = AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL;
++}
++
++static void
++radius_ad_fini(krb5_context kcontext GSSEAP_UNUSED,
++               void *plugin_context GSSEAP_UNUSED)
++{
++    return;
++}
++
++static krb5_error_code
++radius_ad_request_init(krb5_context kcontext GSSEAP_UNUSED,
++                       struct _krb5_authdata_context *context GSSEAP_UNUSED,
++                       void *plugin_context GSSEAP_UNUSED,
++                       void **request_context)
++{
++    struct radius_ad_context *ctx;
++
++    ctx = GSSEAP_CALLOC(1, sizeof(*ctx));
++    if (ctx == NULL)
++        return ENOMEM;
++
++    *request_context = ctx;
++
++    return 0;
++}
++
++static krb5_error_code
++radius_ad_export_authdata(krb5_context kcontext,
++                          struct _krb5_authdata_context *context GSSEAP_UNUSED,
++                          void *plugin_context GSSEAP_UNUSED,
++                          void *request_context,
++                          krb5_flags usage GSSEAP_UNUSED,
++                          krb5_authdata ***out_authdata)
++{
++    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
++    krb5_authdata *data[2];
++    krb5_authdata datum;
++
++    datum.ad_type = KRB5_AUTHDATA_RADIUS_AVP;
++    datum.length = radius_ad->avpdata.length;
++    datum.contents = (krb5_octet *)radius_ad->avpdata.data;
++
++    data[0] = &datum;
++    data[1] = NULL;
++
++    return krb5_copy_authdata(kcontext, data, out_authdata);
++}
++
++static krb5_error_code
++radius_ad_import_authdata(krb5_context kcontext,
++                          struct _krb5_authdata_context *context GSSEAP_UNUSED,
++                          void *plugin_context GSSEAP_UNUSED,
++                          void *request_context,
++                          krb5_authdata **authdata,
++                          krb5_boolean kdc_issued_flag,
++                          krb5_const_principal issuer GSSEAP_UNUSED)
++{
++    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
++
++    krb5_free_data_contents(kcontext, &radius_ad->avpdata);
++    radius_ad->verified = FALSE;
++
++    GSSEAP_ASSERT(authdata[0] != NULL);
++
++    radius_ad->avpdata.data = GSSEAP_MALLOC(authdata[0]->length);
++    if (radius_ad->avpdata.data == NULL)
++        return ENOMEM;
++
++    memcpy(radius_ad->avpdata.data, authdata[0]->contents,
++           authdata[0]->length);
++    radius_ad->avpdata.length = authdata[0]->length;
++
++    radius_ad->verified = kdc_issued_flag;
++
++    return 0;
++}
++
++static void
++radius_ad_request_fini(krb5_context kcontext,
++                       struct _krb5_authdata_context *context GSSEAP_UNUSED,
++                       void *plugin_context GSSEAP_UNUSED,
++                       void *request_context)
++{
++    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
++
++    if (radius_ad != NULL) {
++        krb5_free_data_contents(kcontext, &radius_ad->avpdata);
++        GSSEAP_FREE(radius_ad);
++    }
++}
++
++static krb5_error_code
++radius_ad_get_attribute(krb5_context kcontext GSSEAP_UNUSED,
++                        struct _krb5_authdata_context *context GSSEAP_UNUSED,
++                        void *plugin_context GSSEAP_UNUSED,
++                        void *request_context,
++                        const krb5_data *attribute,
++                        krb5_boolean *authenticated,
++                        krb5_boolean *complete,
++                        krb5_data *value,
++                        krb5_data *display_value GSSEAP_UNUSED,
++                        int *more)
++{
++    struct radius_ad_context *radius_ad = (struct radius_ad_context *)request_context;
++
++    if (attribute->length != radius_ad_attr.length ||
++        memcmp(attribute->data, radius_ad_attr.data,
++               radius_ad_attr.length) != 0)
++        return ENOENT;
++
++    if (radius_ad->avpdata.length == 0)
++        return ENOENT;
++
++    *authenticated = radius_ad->verified;
++    *complete = TRUE;
++    *more = 0;
++
++    value->data = GSSEAP_MALLOC(radius_ad->avpdata.length);
++    if (value->data == NULL)
++        return ENOMEM;
++
++    memcpy(value->data, radius_ad->avpdata.data, radius_ad->avpdata.length);
++    value->length = radius_ad->avpdata.length;
++
++    return 0;
++}
++
++static krb5_error_code
++radius_ad_copy(krb5_context kcontext GSSEAP_UNUSED,
++               struct _krb5_authdata_context *context GSSEAP_UNUSED,
++               void *plugin_context GSSEAP_UNUSED,
++               void *request_context,
++               void *dst_plugin_context GSSEAP_UNUSED,
++               void *dst_request_context)
++{
++    struct radius_ad_context *radius_ad_src =
++        (struct radius_ad_context *)request_context;
++    struct radius_ad_context *radius_ad_dst =
++        (struct radius_ad_context *)dst_request_context;
++
++    radius_ad_dst->avpdata.data = GSSEAP_MALLOC(radius_ad_src->avpdata.length);
++    if (radius_ad_dst->avpdata.data == NULL)
++        return ENOMEM;
++
++    memcpy(radius_ad_dst->avpdata.data, radius_ad_src->avpdata.data,
++           radius_ad_src->avpdata.length);
++    radius_ad_dst->avpdata.length = radius_ad_src->avpdata.length;
++    radius_ad_dst->verified = radius_ad_src->verified;
++
++    return 0;
++}
++
++static krb5_authdatatype radius_ad_ad_types[] =
++    { KRB5_AUTHDATA_RADIUS_AVP, 0 };
++
++krb5plugin_authdata_client_ftable_v0 authdata_client_0 = {
++    "radius_ad",
++    radius_ad_ad_types,
++    radius_ad_init,
++    radius_ad_fini,
++    radius_ad_flags,
++    radius_ad_request_init,
++    radius_ad_request_fini,
++    NULL,
++    radius_ad_get_attribute,
++    NULL,
++    NULL,
++    radius_ad_export_authdata,
++    radius_ad_import_authdata,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    NULL,
++    radius_ad_copy
++};
+diff --git a/mech_eap/util_attr.cpp b/mech_eap/util_attr.cpp
+new file mode 100644
+index 0000000..3bfe785
+--- /dev/null
++++ b/mech_eap/util_attr.cpp
+@@ -0,0 +1,1191 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Attribute provider mechanism.
++ */
++
++#include "gssapiP_eap.h"
++
++#include <typeinfo>
++#include <string>
++#include <sstream>
++#include <exception>
++#include <new>
++
++/* lazy initialisation */
++static GSSEAP_THREAD_ONCE gssEapAttrProvidersInitOnce = GSSEAP_ONCE_INITIALIZER;
++static OM_uint32 gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
++
++GSSEAP_ONCE_CALLBACK(gssEapAttrProvidersInitInternal)
++{
++    OM_uint32 major, minor;
++
++    GSSEAP_ASSERT(gssEapAttrProvidersInitStatus == GSS_S_UNAVAILABLE);
++
++    json_set_alloc_funcs(GSSEAP_MALLOC, GSSEAP_FREE);
++
++    major = gssEapRadiusAttrProviderInit(&minor);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++#ifdef HAVE_OPENSAML
++    major = gssEapSamlAttrProvidersInit(&minor);
++    if (GSS_ERROR(major))
++        goto cleanup;
++#endif
++
++#ifdef HAVE_SHIBRESOLVER
++    /* Allow Shibboleth initialization failure to be non-fatal */
++    gssEapLocalAttrProviderInit(&minor);
++#endif
++
++cleanup:
++#ifdef GSSEAP_DEBUG
++    GSSEAP_ASSERT(major == GSS_S_COMPLETE);
++#endif
++
++    gssEapAttrProvidersInitStatus = major;
++
++    GSSEAP_ONCE_LEAVE;
++}
++
++static OM_uint32
++gssEapAttrProvidersInit(OM_uint32 *minor)
++{
++    GSSEAP_ONCE(&gssEapAttrProvidersInitOnce, gssEapAttrProvidersInitInternal);
++
++    if (GSS_ERROR(gssEapAttrProvidersInitStatus))
++        *minor = GSSEAP_NO_ATTR_PROVIDERS;
++
++    return gssEapAttrProvidersInitStatus;
++}
++
++OM_uint32
++gssEapAttrProvidersFinalize(OM_uint32 *minor)
++{
++    if (gssEapAttrProvidersInitStatus == GSS_S_COMPLETE) {
++#ifdef HAVE_SHIBRESOLVER
++        gssEapLocalAttrProviderFinalize(minor);
++#endif
++#ifdef HAVE_OPENSAML
++        gssEapSamlAttrProvidersFinalize(minor);
++#endif
++        gssEapRadiusAttrProviderFinalize(minor);
++
++        gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++static gss_eap_attr_create_provider gssEapAttrFactories[ATTR_TYPE_MAX + 1];
++
++/*
++ * Register a provider for a particular type and prefix
++ */
++void
++gss_eap_attr_ctx::registerProvider(unsigned int type,
++                                   gss_eap_attr_create_provider factory)
++{
++    GSSEAP_ASSERT(type <= ATTR_TYPE_MAX);
++
++    GSSEAP_ASSERT(gssEapAttrFactories[type] == NULL);
++
++    gssEapAttrFactories[type] = factory;
++}
++
++/*
++ * Unregister a provider
++ */
++void
++gss_eap_attr_ctx::unregisterProvider(unsigned int type)
++{
++    GSSEAP_ASSERT(type <= ATTR_TYPE_MAX);
++
++    gssEapAttrFactories[type] = NULL;
++}
++
++/*
++ * Create an attribute context, that manages instances of providers
++ */
++gss_eap_attr_ctx::gss_eap_attr_ctx(void)
++{
++    m_flags = 0;
++
++    for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider;
++
++        if (gssEapAttrFactories[i] != NULL) {
++            provider = (gssEapAttrFactories[i])();
++        } else {
++            provider = NULL;
++        }
++
++        m_providers[i] = provider;
++    }
++}
++
++/*
++ * Convert an attribute prefix to a type
++ */
++unsigned int
++gss_eap_attr_ctx::attributePrefixToType(const gss_buffer_t prefix) const
++{
++    unsigned int i;
++
++    for (i = ATTR_TYPE_MIN; i < ATTR_TYPE_MAX; i++) {
++        const char *pprefix;
++
++        if (!providerEnabled(i))
++            continue;
++
++        pprefix = m_providers[i]->prefix();
++        if (pprefix == NULL)
++            continue;
++
++        if (strlen(pprefix) == prefix->length &&
++            memcmp(pprefix, prefix->value, prefix->length) == 0)
++            return i;
++    }
++
++    return ATTR_TYPE_LOCAL;
++}
++
++/*
++ * Convert a type to an attribute prefix
++ */
++gss_buffer_desc
++gss_eap_attr_ctx::attributeTypeToPrefix(unsigned int type) const
++{
++    gss_buffer_desc prefix = GSS_C_EMPTY_BUFFER;
++
++    if (type < ATTR_TYPE_MIN || type >= ATTR_TYPE_MAX)
++        return prefix;
++
++    if (!providerEnabled(type))
++        return prefix;
++
++    prefix.value = (void *)m_providers[type]->prefix();
++    if (prefix.value != NULL)
++        prefix.length = strlen((char *)prefix.value);
++
++    return prefix;
++}
++
++bool
++gss_eap_attr_ctx::providerEnabled(unsigned int type) const
++{
++    if (type == ATTR_TYPE_LOCAL &&
++        (m_flags & ATTR_FLAG_DISABLE_LOCAL))
++        return false;
++
++    if (m_providers[type] == NULL)
++        return false;
++
++    return true;
++}
++
++void
++gss_eap_attr_ctx::releaseProvider(unsigned int type)
++{
++    delete m_providers[type];
++    m_providers[type] = NULL;
++}
++
++/*
++ * Initialize a context from an existing context.
++ */
++bool
++gss_eap_attr_ctx::initWithExistingContext(const gss_eap_attr_ctx *manager)
++{
++    bool ret = true;
++
++    m_flags = manager->m_flags;
++
++    for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider;
++
++        if (!providerEnabled(i)) {
++            releaseProvider(i);
++            continue;
++        }
++
++        provider = m_providers[i];
++
++        ret = provider->initWithExistingContext(this,
++                                                manager->m_providers[i]);
++        if (ret == false) {
++            releaseProvider(i);
++            break;
++        }
++    }
++
++    return ret;
++}
++
++/*
++ * Initialize a context from a GSS credential and context.
++ */
++bool
++gss_eap_attr_ctx::initWithGssContext(const gss_cred_id_t cred,
++                                     const gss_ctx_id_t ctx)
++{
++    bool ret = true;
++
++    if (cred != GSS_C_NO_CREDENTIAL &&
++        (cred->flags & GSS_EAP_DISABLE_LOCAL_ATTRS_FLAG)) {
++        m_flags |= ATTR_FLAG_DISABLE_LOCAL;
++    }
++
++    for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider;
++
++        if (!providerEnabled(i)) {
++            releaseProvider(i);
++            continue;
++        }
++
++        provider = m_providers[i];
++
++        ret = provider->initWithGssContext(this, cred, ctx);
++        if (ret == false) {
++            releaseProvider(i);
++            break;
++        }
++    }
++
++    return ret;
++}
++
++bool
++gss_eap_attr_ctx::initWithJsonObject(JSONObject &obj)
++{
++    bool ret = false;
++    bool foundSource[ATTR_TYPE_MAX + 1];
++    unsigned int type;
++
++    for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++)
++        foundSource[type] = false;
++
++    if (obj["version"].integer() != 1)
++        return false;
++
++    m_flags = obj["flags"].integer();
++
++    JSONObject sources = obj["sources"];
++
++    /* Initialize providers from serialized state */
++    for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++) {
++        gss_eap_attr_provider *provider;
++        const char *key;
++
++        if (!providerEnabled(type)) {
++            releaseProvider(type);
++            continue;
++        }
++
++        provider = m_providers[type];
++        key = provider->name();
++        if (key == NULL)
++            continue;
++
++        JSONObject source = sources.get(key);
++        if (!source.isNull() &&
++            !provider->initWithJsonObject(this, source)) {
++            releaseProvider(type);
++            return false;
++        }
++
++        foundSource[type] = true;
++    }
++
++    /* Initialize remaining providers from initialized providers */
++    for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++) {
++        gss_eap_attr_provider *provider;
++
++        if (foundSource[type] || !providerEnabled(type))
++            continue;
++
++        provider = m_providers[type];
++
++        ret = provider->initWithGssContext(this,
++                                           GSS_C_NO_CREDENTIAL,
++                                           GSS_C_NO_CONTEXT);
++        if (ret == false) {
++            releaseProvider(type);
++            return false;
++        }
++    }
++
++    return true;
++}
++
++JSONObject
++gss_eap_attr_ctx::jsonRepresentation(void) const
++{
++    JSONObject obj, sources;
++    unsigned int i;
++
++    obj.set("version", 1);
++    obj.set("flags", m_flags);
++
++    for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider;
++        const char *key;
++
++        provider = m_providers[i];
++        if (provider == NULL)
++            continue; /* provider not initialised */
++
++        key = provider->name();
++        if (key == NULL)
++            continue; /* provider does not have state */
++
++        JSONObject source = provider->jsonRepresentation();
++        sources.set(key, source);
++    }
++
++    obj.set("sources", sources);
++
++    return obj;
++}
++
++/*
++ * Initialize a context from an exported context or name token
++ */
++bool
++gss_eap_attr_ctx::initWithBuffer(const gss_buffer_t buffer)
++{
++    OM_uint32 major, minor;
++    bool ret;
++    char *s;
++    json_error_t error;
++
++    major = bufferToString(&minor, buffer, &s);
++    if (GSS_ERROR(major))
++        return false;
++
++    JSONObject obj = JSONObject::load(s, 0, &error);
++    if (!obj.isNull()) {
++        ret = initWithJsonObject(obj);
++    } else
++        ret = false;
++
++    GSSEAP_FREE(s);
++
++    return ret;
++}
++
++gss_eap_attr_ctx::~gss_eap_attr_ctx(void)
++{
++    for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++)
++        delete m_providers[i];
++}
++
++/*
++ * Locate provider for a given type
++ */
++gss_eap_attr_provider *
++gss_eap_attr_ctx::getProvider(unsigned int type) const
++{
++    GSSEAP_ASSERT(type >= ATTR_TYPE_MIN && type <= ATTR_TYPE_MAX);
++    return m_providers[type];
++}
++
++/*
++ * Get primary provider. Only the primary provider is serialised when
++ * gss_export_sec_context() or gss_export_name_composite() is called.
++ */
++gss_eap_attr_provider *
++gss_eap_attr_ctx::getPrimaryProvider(void) const
++{
++    return m_providers[ATTR_TYPE_MIN];
++}
++
++/*
++ * Set an attribute
++ */
++bool
++gss_eap_attr_ctx::setAttribute(int complete,
++                               const gss_buffer_t attr,
++                               const gss_buffer_t value)
++{
++    gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
++    unsigned int type;
++    gss_eap_attr_provider *provider;
++    bool ret = false;
++
++    decomposeAttributeName(attr, &type, &suffix);
++
++    provider = m_providers[type];
++    if (provider != NULL) {
++        ret = provider->setAttribute(complete,
++                                     (type == ATTR_TYPE_LOCAL) ? attr : &suffix,
++                                     value);
++    }
++
++    return ret;
++}
++
++/*
++ * Delete an attrbiute
++ */
++bool
++gss_eap_attr_ctx::deleteAttribute(const gss_buffer_t attr)
++{
++    gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
++    unsigned int type;
++    gss_eap_attr_provider *provider;
++    bool ret = false;
++
++    decomposeAttributeName(attr, &type, &suffix);
++
++    provider = m_providers[type];
++    if (provider != NULL) {
++        ret = provider->deleteAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix);
++    }
++
++    return ret;
++}
++
++/*
++ * Enumerate attribute types with callback
++ */
++bool
++gss_eap_attr_ctx::getAttributeTypes(gss_eap_attr_enumeration_cb cb, void *data) const
++{
++    bool ret = false;
++    size_t i;
++
++    for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider = m_providers[i];
++
++        if (provider == NULL)
++            continue;
++
++        ret = provider->getAttributeTypes(cb, data);
++        if (ret == false)
++            break;
++    }
++
++    return ret;
++}
++
++struct eap_gss_get_attr_types_args {
++    unsigned int type;
++    gss_buffer_set_t attrs;
++};
++
++static bool
++addAttribute(const gss_eap_attr_ctx *manager,
++             const gss_eap_attr_provider *provider GSSEAP_UNUSED,
++             const gss_buffer_t attribute,
++             void *data)
++{
++    eap_gss_get_attr_types_args *args = (eap_gss_get_attr_types_args *)data;
++    gss_buffer_desc qualified;
++    OM_uint32 major, minor;
++
++    if (args->type != ATTR_TYPE_LOCAL) {
++        manager->composeAttributeName(args->type, attribute, &qualified);
++        major = gss_add_buffer_set_member(&minor, &qualified, &args->attrs);
++        gss_release_buffer(&minor, &qualified);
++    } else {
++        major = gss_add_buffer_set_member(&minor, attribute, &args->attrs);
++    }
++
++    return GSS_ERROR(major) == false;
++}
++
++/*
++ * Enumerate attribute types, output is buffer set
++ */
++bool
++gss_eap_attr_ctx::getAttributeTypes(gss_buffer_set_t *attrs)
++{
++    eap_gss_get_attr_types_args args;
++    OM_uint32 major, minor;
++    bool ret = false;
++    unsigned int i;
++
++    major = gss_create_empty_buffer_set(&minor, attrs);
++    if (GSS_ERROR(major))
++        throw std::bad_alloc();
++
++    args.attrs = *attrs;
++
++    for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider = m_providers[i];
++
++        args.type = i;
++
++        if (provider == NULL)
++            continue;
++
++        ret = provider->getAttributeTypes(addAttribute, (void *)&args);
++        if (ret == false)
++            break;
++    }
++
++    if (ret == false)
++        gss_release_buffer_set(&minor, attrs);
++
++    return ret;
++}
++
++/*
++ * Get attribute with given name
++ */
++bool
++gss_eap_attr_ctx::getAttribute(const gss_buffer_t attr,
++                               int *authenticated,
++                               int *complete,
++                               gss_buffer_t value,
++                               gss_buffer_t display_value,
++                               int *more) const
++{
++    gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
++    unsigned int type;
++    gss_eap_attr_provider *provider;
++    bool ret;
++
++    decomposeAttributeName(attr, &type, &suffix);
++
++    provider = m_providers[type];
++    if (provider == NULL)
++        return false;
++
++    ret = provider->getAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix,
++                                 authenticated, complete,
++                                 value, display_value, more);
++
++    return ret;
++}
++
++/*
++ * Map attribute context to C++ object
++ */
++gss_any_t
++gss_eap_attr_ctx::mapToAny(int authenticated,
++                           gss_buffer_t type_id) const
++{
++    unsigned int type;
++    gss_eap_attr_provider *provider;
++    gss_buffer_desc suffix;
++
++    decomposeAttributeName(type_id, &type, &suffix);
++
++    provider = m_providers[type];
++    if (provider == NULL)
++        return (gss_any_t)NULL;
++
++    return provider->mapToAny(authenticated, &suffix);
++}
++
++/*
++ * Release mapped context
++ */
++void
++gss_eap_attr_ctx::releaseAnyNameMapping(gss_buffer_t type_id,
++                                        gss_any_t input) const
++{
++    unsigned int type;
++    gss_eap_attr_provider *provider;
++    gss_buffer_desc suffix;
++
++    decomposeAttributeName(type_id, &type, &suffix);
++
++    provider = m_providers[type];
++    if (provider != NULL)
++        provider->releaseAnyNameMapping(&suffix, input);
++}
++
++/*
++ * Export attribute context to buffer
++ */
++void
++gss_eap_attr_ctx::exportToBuffer(gss_buffer_t buffer) const
++{
++    OM_uint32 minor;
++    char *s;
++
++    JSONObject obj = jsonRepresentation();
++
++#if 0
++    obj.dump(stdout);
++#endif
++
++    s = obj.dump(JSON_COMPACT);
++
++    if (GSS_ERROR(makeStringBuffer(&minor, s, buffer)))
++        throw std::bad_alloc();
++}
++
++/*
++ * Return soonest expiry time of providers
++ */
++time_t
++gss_eap_attr_ctx::getExpiryTime(void) const
++{
++    unsigned int i;
++    time_t expiryTime = 0;
++
++    for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider = m_providers[i];
++        time_t providerExpiryTime;
++
++        if (provider == NULL)
++            continue;
++
++        providerExpiryTime = provider->getExpiryTime();
++        if (providerExpiryTime == 0)
++            continue;
++
++        if (expiryTime == 0 || providerExpiryTime < expiryTime)
++            expiryTime = providerExpiryTime;
++    }
++
++    return expiryTime;
++}
++
++OM_uint32
++gss_eap_attr_ctx::mapException(OM_uint32 *minor, std::exception &e) const
++{
++    unsigned int i;
++    OM_uint32 major;
++
++    /* Errors we handle ourselves */
++    if (typeid(e) == typeid(std::bad_alloc)) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    } else if (typeid(e) == typeid(JSONException)) {
++        major = GSS_S_BAD_NAME;
++        *minor = GSSEAP_BAD_ATTR_TOKEN;
++        gssEapSaveStatusInfo(*minor, "%s", e.what());
++        goto cleanup;
++    }
++
++    /* Errors we delegate to providers */
++    major = GSS_S_CONTINUE_NEEDED;
++
++    for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
++        gss_eap_attr_provider *provider = m_providers[i];
++
++        if (provider == NULL)
++            continue;
++
++        major = provider->mapException(minor, e);
++        if (major != GSS_S_CONTINUE_NEEDED)
++            break;
++    }
++
++    if (major == GSS_S_CONTINUE_NEEDED) {
++        *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
++        major = GSS_S_FAILURE;
++    }
++
++cleanup:
++    GSSEAP_ASSERT(GSS_ERROR(major));
++
++    return major;
++}
++
++/*
++ * Decompose attribute name into prefix and suffix
++ */
++void
++gss_eap_attr_ctx::decomposeAttributeName(const gss_buffer_t attribute,
++                                         gss_buffer_t prefix,
++                                         gss_buffer_t suffix)
++{
++    char *p = NULL;
++    size_t i;
++
++    for (i = 0; i < attribute->length; i++) {
++        if (((char *)attribute->value)[i] == ' ') {
++            p = (char *)attribute->value + i + 1;
++            break;
++        }
++    }
++
++    prefix->value = attribute->value;
++    prefix->length = i;
++
++    if (p != NULL && *p != '\0')  {
++        suffix->length = attribute->length - 1 - prefix->length;
++        suffix->value = p;
++    } else {
++        suffix->length = 0;
++        suffix->value = NULL;
++    }
++}
++
++/*
++ * Decompose attribute name into type and suffix
++ */
++void
++gss_eap_attr_ctx::decomposeAttributeName(const gss_buffer_t attribute,
++                                         unsigned int *type,
++                                         gss_buffer_t suffix) const
++{
++    gss_buffer_desc prefix = GSS_C_EMPTY_BUFFER;
++
++    decomposeAttributeName(attribute, &prefix, suffix);
++    *type = attributePrefixToType(&prefix);
++}
++
++/*
++ * Compose attribute name from prefix, suffix; returns C++ string
++ */
++std::string
++gss_eap_attr_ctx::composeAttributeName(const gss_buffer_t prefix,
++                                       const gss_buffer_t suffix)
++{
++    std::string str;
++
++    if (prefix == GSS_C_NO_BUFFER || prefix->length == 0)
++        return str;
++
++    str.append((const char *)prefix->value, prefix->length);
++
++    if (suffix != GSS_C_NO_BUFFER) {
++        str.append(" ");
++        str.append((const char *)suffix->value, suffix->length);
++    }
++
++    return str;
++}
++
++/*
++ * Compose attribute name from type, suffix; returns C++ string
++ */
++std::string
++gss_eap_attr_ctx::composeAttributeName(unsigned int type,
++                                       const gss_buffer_t suffix)
++{
++    gss_buffer_desc prefix = attributeTypeToPrefix(type);
++
++    return composeAttributeName(&prefix, suffix);
++}
++
++/*
++ * Compose attribute name from prefix, suffix; returns GSS buffer
++ */
++void
++gss_eap_attr_ctx::composeAttributeName(const gss_buffer_t prefix,
++                                       const gss_buffer_t suffix,
++                                       gss_buffer_t attribute)
++{
++    std::string str = composeAttributeName(prefix, suffix);
++
++    if (str.length() != 0) {
++        return duplicateBuffer(str, attribute);
++    } else {
++        attribute->length = 0;
++        attribute->value = NULL;
++    }
++}
++
++/*
++ * Compose attribute name from type, suffix; returns GSS buffer
++ */
++void
++gss_eap_attr_ctx::composeAttributeName(unsigned int type,
++                                       const gss_buffer_t suffix,
++                                       gss_buffer_t attribute) const
++{
++    gss_buffer_desc prefix = attributeTypeToPrefix(type);
++
++    return composeAttributeName(&prefix, suffix, attribute);
++}
++
++/*
++ * C wrappers
++ */
++OM_uint32
++gssEapInquireName(OM_uint32 *minor,
++                  gss_name_t name,
++                  int *name_is_MN,
++                  gss_OID *MN_mech,
++                  gss_buffer_set_t *attrs)
++{
++    OM_uint32 major;
++
++    if (name_is_MN != NULL)
++        *name_is_MN = (name->mechanismUsed != GSS_C_NULL_OID);
++
++    if (MN_mech != NULL) {
++        major = gssEapCanonicalizeOid(minor, name->mechanismUsed,
++                                      OID_FLAG_NULL_VALID, MN_mech);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    if (name->attrCtx == NULL) {
++        *minor = GSSEAP_NO_ATTR_CONTEXT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor))) {
++        return GSS_S_UNAVAILABLE;
++    }
++
++    try {
++        if (!name->attrCtx->getAttributeTypes(attrs)) {
++            *minor = GSSEAP_NO_ATTR_CONTEXT;
++            return GSS_S_UNAVAILABLE;
++        }
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapGetNameAttribute(OM_uint32 *minor,
++                       gss_name_t name,
++                       gss_buffer_t attr,
++                       int *authenticated,
++                       int *complete,
++                       gss_buffer_t value,
++                       gss_buffer_t display_value,
++                       int *more)
++{
++    if (authenticated != NULL)
++        *authenticated = 0;
++    if (complete != NULL)
++        *complete = 0;
++
++    if (value != NULL) {
++        value->length = 0;
++        value->value = NULL;
++    }
++
++    if (display_value != NULL) {
++        display_value->length = 0;
++        display_value->value = NULL;
++    }
++
++    if (name->attrCtx == NULL) {
++        *minor = GSSEAP_NO_ATTR_CONTEXT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor))) {
++        return GSS_S_UNAVAILABLE;
++    }
++
++    try {
++        if (!name->attrCtx->getAttribute(attr, authenticated, complete,
++                                         value, display_value, more)) {
++            *minor = GSSEAP_NO_SUCH_ATTR;
++            gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
++                                 (int)attr->length, (char *)attr->value);
++            return GSS_S_UNAVAILABLE;
++        }
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapDeleteNameAttribute(OM_uint32 *minor,
++                          gss_name_t name,
++                          gss_buffer_t attr)
++{
++    if (name->attrCtx == NULL) {
++        *minor = GSSEAP_NO_ATTR_CONTEXT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    try {
++        if (!name->attrCtx->deleteAttribute(attr)) {
++            *minor = GSSEAP_NO_SUCH_ATTR;
++            gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
++                                 (int)attr->length, (char *)attr->value);
++            return GSS_S_UNAVAILABLE;
++        }
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapSetNameAttribute(OM_uint32 *minor,
++                       gss_name_t name,
++                       int complete,
++                       gss_buffer_t attr,
++                       gss_buffer_t value)
++{
++    if (name->attrCtx == NULL) {
++        *minor = GSSEAP_NO_ATTR_CONTEXT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    try {
++        if (!name->attrCtx->setAttribute(complete, attr, value)) {
++             *minor = GSSEAP_NO_SUCH_ATTR;
++            gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
++                                 (int)attr->length, (char *)attr->value);
++            return GSS_S_UNAVAILABLE;
++        }
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapExportAttrContext(OM_uint32 *minor,
++                        gss_name_t name,
++                        gss_buffer_t buffer)
++{
++    if (name->attrCtx == NULL) {
++        buffer->length = 0;
++        buffer->value = NULL;
++
++        return GSS_S_COMPLETE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    try {
++        name->attrCtx->exportToBuffer(buffer);
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapImportAttrContext(OM_uint32 *minor,
++                        gss_buffer_t buffer,
++                        gss_name_t name)
++{
++    gss_eap_attr_ctx *ctx = NULL;
++    OM_uint32 major = GSS_S_FAILURE;
++
++    GSSEAP_ASSERT(name->attrCtx == NULL);
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    if (buffer->length == 0)
++        return GSS_S_COMPLETE;
++
++    try {
++        ctx = new gss_eap_attr_ctx();
++
++        if (ctx->initWithBuffer(buffer)) {
++            name->attrCtx = ctx;
++            major = GSS_S_COMPLETE;
++            *minor = 0;
++        } else {
++            major = GSS_S_BAD_NAME;
++            *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
++        }
++    } catch (std::exception &e) {
++        if (ctx != NULL)
++            major = ctx->mapException(minor, e);
++    }
++
++    GSSEAP_ASSERT(major == GSS_S_COMPLETE || name->attrCtx == NULL);
++
++    if (GSS_ERROR(major))
++        delete ctx;
++
++    return major;
++}
++
++OM_uint32
++gssEapDuplicateAttrContext(OM_uint32 *minor,
++                           gss_name_t in,
++                           gss_name_t out)
++{
++    gss_eap_attr_ctx *ctx = NULL;
++    OM_uint32 major = GSS_S_FAILURE;
++
++    GSSEAP_ASSERT(out->attrCtx == NULL);
++
++    if (in->attrCtx == NULL) {
++        *minor = 0;
++        return GSS_S_COMPLETE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    try {
++        ctx = new gss_eap_attr_ctx();
++
++        if (ctx->initWithExistingContext(in->attrCtx)) {
++            out->attrCtx = ctx;
++            major = GSS_S_COMPLETE;
++            *minor = 0;
++        } else {
++            major = GSS_S_FAILURE;
++            *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
++        }
++    } catch (std::exception &e) {
++        major = in->attrCtx->mapException(minor, e);
++    }
++
++    GSSEAP_ASSERT(major == GSS_S_COMPLETE || out->attrCtx == NULL);
++
++    if (GSS_ERROR(major))
++        delete ctx;
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapMapNameToAny(OM_uint32 *minor,
++                   gss_name_t name,
++                   int authenticated,
++                   gss_buffer_t type_id,
++                   gss_any_t *output)
++{
++    if (name->attrCtx == NULL) {
++        *minor = GSSEAP_NO_ATTR_CONTEXT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    try {
++        *output = name->attrCtx->mapToAny(authenticated, type_id);
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapReleaseAnyNameMapping(OM_uint32 *minor,
++                            gss_name_t name,
++                            gss_buffer_t type_id,
++                            gss_any_t *input)
++{
++    if (name->attrCtx == NULL) {
++        *minor = GSSEAP_NO_ATTR_CONTEXT;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
++        return GSS_S_UNAVAILABLE;
++
++    try {
++        if (*input != NULL)
++            name->attrCtx->releaseAnyNameMapping(type_id, *input);
++        *input = NULL;
++    } catch (std::exception &e) {
++        return name->attrCtx->mapException(minor, e);
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapReleaseAttrContext(OM_uint32 *minor,
++                         gss_name_t name)
++{
++    if (name->attrCtx != NULL)
++        delete name->attrCtx;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/*
++ * Public accessor for initialisng a context from a GSS context. Also
++ * sets expiry time on GSS context as a side-effect.
++ */
++OM_uint32
++gssEapCreateAttrContext(OM_uint32 *minor,
++                        gss_cred_id_t gssCred,
++                        gss_ctx_id_t gssCtx,
++                        struct gss_eap_attr_ctx **pAttrContext,
++                        time_t *pExpiryTime)
++{
++    gss_eap_attr_ctx *ctx = NULL;
++    OM_uint32 major;
++
++    GSSEAP_ASSERT(gssCtx != GSS_C_NO_CONTEXT);
++
++    *pAttrContext = NULL;
++
++    major = gssEapAttrProvidersInit(minor);
++    if (GSS_ERROR(major))
++        return major;
++
++    try {
++        /* Set *pAttrContext here to for reentrancy */
++        *pAttrContext = ctx = new gss_eap_attr_ctx();
++
++        if (ctx->initWithGssContext(gssCred, gssCtx)) {
++            *pExpiryTime = ctx->getExpiryTime();
++            major = GSS_S_COMPLETE;
++            *minor = 0;
++        } else {
++            major = GSS_S_FAILURE;
++            *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
++        }
++    } catch (std::exception &e) {
++        if (ctx != NULL)
++            major = ctx->mapException(minor, e);
++    }
++
++    if (GSS_ERROR(major)) {
++        delete ctx;
++        *pAttrContext = NULL;
++    }
++
++    return major;
++}
+diff --git a/mech_eap/util_attr.h b/mech_eap/util_attr.h
+new file mode 100644
+index 0000000..2af0850
+--- /dev/null
++++ b/mech_eap/util_attr.h
+@@ -0,0 +1,389 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Attribute provider interface.
++ */
++
++#ifndef _UTIL_ATTR_H_
++#define _UTIL_ATTR_H_ 1
++
++#ifdef __cplusplus
++#include <string>
++#include <new>
++
++using namespace gss_eap_util;
++
++struct gss_eap_attr_provider;
++struct gss_eap_attr_ctx;
++
++typedef bool
++(*gss_eap_attr_enumeration_cb)(const gss_eap_attr_ctx *ctx,
++                               const gss_eap_attr_provider *source,
++                               const gss_buffer_t attribute,
++                               void *data);
++
++#define ATTR_TYPE_RADIUS            0U                  /* RADIUS AVPs */
++#ifdef HAVE_OPENSAML
++#define ATTR_TYPE_SAML_ASSERTION    1U                  /* SAML assertion */
++#define ATTR_TYPE_SAML              2U                  /* SAML attributes */
++#endif
++#define ATTR_TYPE_LOCAL             3U                  /* Local attributes */
++#define ATTR_TYPE_MIN               ATTR_TYPE_RADIUS
++#define ATTR_TYPE_MAX               ATTR_TYPE_LOCAL
++
++#define ATTR_FLAG_DISABLE_LOCAL     0x00000001
++
++/*
++ * Attribute provider: this represents a source of attributes derived
++ * from the security context.
++ */
++struct gss_eap_attr_provider
++{
++public:
++    gss_eap_attr_provider(void) {}
++    virtual ~gss_eap_attr_provider(void) {}
++
++    bool initWithManager(const gss_eap_attr_ctx *manager)
++    {
++        m_manager = manager;
++        return true;
++    }
++
++    virtual bool initWithExistingContext(const gss_eap_attr_ctx *manager,
++                                         const gss_eap_attr_provider *ctx GSSEAP_UNUSED)
++    {
++        return initWithManager(manager);
++    }
++
++    virtual bool initWithGssContext(const gss_eap_attr_ctx *manager,
++                                    const gss_cred_id_t cred GSSEAP_UNUSED,
++                                    const gss_ctx_id_t ctx GSSEAP_UNUSED)
++    {
++        return initWithManager(manager);
++    }
++
++    virtual bool getAttributeTypes(gss_eap_attr_enumeration_cb GSSEAP_UNUSED,
++                                   void *data GSSEAP_UNUSED) const
++    {
++        return false;
++    }
++
++    virtual bool setAttribute(int complete GSSEAP_UNUSED,
++                              const gss_buffer_t attr GSSEAP_UNUSED,
++                              const gss_buffer_t value GSSEAP_UNUSED)
++    {
++        return false;
++    }
++
++    virtual bool deleteAttribute(const gss_buffer_t value GSSEAP_UNUSED)
++    {
++        return false;
++    }
++
++    virtual bool getAttribute(const gss_buffer_t attr GSSEAP_UNUSED,
++                              int *authenticated GSSEAP_UNUSED,
++                              int *complete GSSEAP_UNUSED,
++                              gss_buffer_t value GSSEAP_UNUSED,
++                              gss_buffer_t display_value GSSEAP_UNUSED,
++                              int *more GSSEAP_UNUSED) const
++    {
++        return false;
++    }
++
++    virtual gss_any_t mapToAny(int authenticated GSSEAP_UNUSED,
++                               gss_buffer_t type_id GSSEAP_UNUSED) const
++    {
++        return NULL;
++    }
++
++    virtual void releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
++                                       gss_any_t input GSSEAP_UNUSED) const
++    {
++    }
++
++    /* prefix to be prepended to attributes emitted by gss_get_name_attribute */
++    virtual const char *prefix(void) const
++    {
++        return NULL;
++    }
++
++    /* optional key for storing JSON dictionary */
++    virtual const char *name(void) const
++    {
++        return NULL;
++    }
++
++    virtual bool initWithJsonObject(const gss_eap_attr_ctx *manager,
++                                    JSONObject &object GSSEAP_UNUSED)
++    {
++        return initWithManager(manager);
++    }
++
++
++    virtual JSONObject jsonRepresentation(void) const
++    {
++        return JSONObject::null();
++    }
++
++    virtual time_t getExpiryTime(void) const { return 0; }
++
++    virtual OM_uint32 mapException(OM_uint32 *minor GSSEAP_UNUSED,
++                                   std::exception &e GSSEAP_UNUSED) const
++    {
++        return GSS_S_CONTINUE_NEEDED;
++    }
++
++    static bool init(void) { return true; }
++    static void finalize(void) {}
++
++    static gss_eap_attr_provider *createAttrContext(void) { return NULL; }
++
++protected:
++    const gss_eap_attr_ctx *m_manager;
++
++private:
++    /* make non-copyable */
++    gss_eap_attr_provider(const gss_eap_attr_provider&);
++    gss_eap_attr_provider& operator=(const gss_eap_attr_provider&);
++};
++
++typedef gss_eap_attr_provider *(*gss_eap_attr_create_provider)(void);
++
++/*
++ * Attribute context: this manages a set of providers for a given
++ * security context.
++ */
++struct gss_eap_attr_ctx
++{
++public:
++    gss_eap_attr_ctx(void);
++    ~gss_eap_attr_ctx(void);
++
++    bool initWithExistingContext(const gss_eap_attr_ctx *manager);
++    bool initWithGssContext(const gss_cred_id_t cred,
++                            const gss_ctx_id_t ctx);
++
++    bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const;
++    bool getAttributeTypes(gss_buffer_set_t *attrs);
++
++    bool setAttribute(int complete,
++                      const gss_buffer_t attr,
++                      const gss_buffer_t value);
++    bool deleteAttribute(const gss_buffer_t value);
++    bool getAttribute(const gss_buffer_t attr,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    gss_any_t mapToAny(int authenticated,
++                       gss_buffer_t type_id) const;
++    void releaseAnyNameMapping(gss_buffer_t type_id,
++                               gss_any_t input) const;
++
++    void exportToBuffer(gss_buffer_t buffer) const;
++    bool initWithBuffer(const gss_buffer_t buffer);
++
++    static std::string
++    composeAttributeName(const gss_buffer_t prefix,
++                         const gss_buffer_t suffix);
++    static void
++    decomposeAttributeName(const gss_buffer_t attribute,
++                           gss_buffer_t prefix,
++                           gss_buffer_t suffix);
++    static void
++    composeAttributeName(const gss_buffer_t prefix,
++                         const gss_buffer_t suffix,
++                         gss_buffer_t attribute);
++
++    std::string
++    composeAttributeName(unsigned int type,
++                         const gss_buffer_t suffix);
++    void
++    decomposeAttributeName(const gss_buffer_t attribute,
++                           unsigned int *type,
++                           gss_buffer_t suffix) const;
++    void
++    composeAttributeName(unsigned int type,
++                         const gss_buffer_t suffix,
++                         gss_buffer_t attribute) const;
++
++    gss_eap_attr_provider *getProvider(unsigned int type) const;
++
++    static void
++    registerProvider(unsigned int type,
++                     gss_eap_attr_create_provider factory);
++    static void
++    unregisterProvider(unsigned int type);
++
++    time_t getExpiryTime(void) const;
++    OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const;
++
++private:
++    bool providerEnabled(unsigned int type) const;
++    void releaseProvider(unsigned int type);
++
++    unsigned int attributePrefixToType(const gss_buffer_t prefix) const;
++    gss_buffer_desc attributeTypeToPrefix(unsigned int type) const;
++
++    bool initWithJsonObject(JSONObject &object);
++    JSONObject jsonRepresentation(void) const;
++
++    gss_eap_attr_provider *getPrimaryProvider(void) const;
++
++    /* make non-copyable */
++    gss_eap_attr_ctx(const gss_eap_attr_ctx&);
++    gss_eap_attr_ctx& operator=(const gss_eap_attr_ctx&);
++
++    uint32_t m_flags;
++    gss_eap_attr_provider *m_providers[ATTR_TYPE_MAX + 1];
++};
++
++#endif /* __cplusplus */
++
++#include "util_radius.h"
++#include "util_saml.h"
++#include "util_shib.h"
++
++#ifdef __cplusplus
++
++static inline void
++duplicateBuffer(gss_buffer_desc &src, gss_buffer_t dst)
++{
++    OM_uint32 minor;
++
++    if (GSS_ERROR(duplicateBuffer(&minor, &src, dst)))
++        throw std::bad_alloc();
++}
++
++static inline void
++duplicateBuffer(std::string &str, gss_buffer_t buffer)
++{
++    gss_buffer_desc tmp;
++
++    tmp.length = str.length();
++    tmp.value = (char *)str.c_str();
++
++    duplicateBuffer(tmp, buffer);
++}
++
++#else
++struct gss_eap_attr_ctx;
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * C wrappers for attribute context functions. These match their
++ * GSS naming extension equivalents. The caller is required to
++ * obtain the name mutex.
++ */
++
++OM_uint32
++gssEapCreateAttrContext(OM_uint32 *minor,
++                        gss_cred_id_t acceptorCred,
++                        gss_ctx_id_t acceptorCtx,
++                        struct gss_eap_attr_ctx **pAttrCtx,
++                        time_t *pExpiryTime);
++
++OM_uint32
++gssEapInquireName(OM_uint32 *minor,
++                  gss_name_t name,
++                  int *name_is_MN,
++                  gss_OID *MN_mech,
++                  gss_buffer_set_t *attrs);
++
++OM_uint32
++gssEapGetNameAttribute(OM_uint32 *minor,
++                       gss_name_t name,
++                       gss_buffer_t attr,
++                       int *authenticated,
++                       int *complete,
++                       gss_buffer_t value,
++                       gss_buffer_t display_value,
++                       int *more);
++
++OM_uint32
++gssEapDeleteNameAttribute(OM_uint32 *minor,
++                          gss_name_t name,
++                          gss_buffer_t attr);
++
++OM_uint32
++gssEapSetNameAttribute(OM_uint32 *minor,
++                       gss_name_t name,
++                       int complete,
++                       gss_buffer_t attr,
++                       gss_buffer_t value);
++
++OM_uint32
++gssEapExportAttrContext(OM_uint32 *minor,
++                        gss_name_t name,
++                        gss_buffer_t buffer);
++
++OM_uint32
++gssEapImportAttrContext(OM_uint32 *minor,
++                        gss_buffer_t buffer,
++                        gss_name_t name);
++
++OM_uint32
++gssEapDuplicateAttrContext(OM_uint32 *minor,
++                           gss_name_t in,
++                           gss_name_t out);
++
++OM_uint32
++gssEapMapNameToAny(OM_uint32 *minor,
++                   gss_name_t name,
++                   int authenticated,
++                   gss_buffer_t type_id,
++                   gss_any_t *output);
++
++OM_uint32
++gssEapReleaseAnyNameMapping(OM_uint32 *minor,
++                            gss_name_t name,
++                            gss_buffer_t type_id,
++                            gss_any_t *input);
++
++OM_uint32
++gssEapReleaseAttrContext(OM_uint32 *minor,
++                         gss_name_t name);
++
++OM_uint32
++gssEapAttrProvidersFinalize(OM_uint32 *minor);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _UTIL_ATTR_H_ */
+diff --git a/mech_eap/util_base64.c b/mech_eap/util_base64.c
+new file mode 100644
+index 0000000..aaa1ea8
+--- /dev/null
++++ b/mech_eap/util_base64.c
+@@ -0,0 +1,161 @@
++/*
++ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
++ * (Royal Institute of Technology, Stockholm, Sweden).
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of the Institute nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++static const char base64_chars[] =
++    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
++
++static int
++pos(char c)
++{
++    const char *p;
++    for (p = base64_chars; *p; p++)
++      if (*p == c)
++          return p - base64_chars;
++    return -1;
++}
++
++ssize_t
++base64Encode(const void *data, int size, char **str)
++{
++    char *s, *p;
++    int i;
++    int c;
++    const unsigned char *q;
++
++    if (size > INT_MAX/4 || size < 0) {
++      *str = NULL;
++      return -1;
++    }
++
++    p = s = (char *)GSSEAP_MALLOC(BASE64_EXPAND(size));
++    if (p == NULL) {
++        *str = NULL;
++      return -1;
++    }
++    q = (const unsigned char *) data;
++
++    for (i = 0; i < size;) {
++      c = q[i++];
++      c *= 256;
++      if (i < size)
++          c += q[i];
++      i++;
++      c *= 256;
++      if (i < size)
++          c += q[i];
++      i++;
++      p[0] = base64_chars[(c & 0x00fc0000) >> 18];
++      p[1] = base64_chars[(c & 0x0003f000) >> 12];
++      p[2] = base64_chars[(c & 0x00000fc0) >> 6];
++      p[3] = base64_chars[(c & 0x0000003f) >> 0];
++      if (i > size)
++          p[3] = '=';
++      if (i > size + 1)
++          p[2] = '=';
++      p += 4;
++    }
++    *p = 0;
++    *str = s;
++    return strlen(s);
++}
++
++#define DECODE_ERROR 0xffffffff
++
++static unsigned int
++token_decode(const char *token)
++{
++    int i;
++    unsigned int val = 0;
++    int marker = 0;
++    if (strlen(token) < 4)
++      return DECODE_ERROR;
++    for (i = 0; i < 4; i++) {
++      val *= 64;
++      if (token[i] == '=')
++          marker++;
++      else if (marker > 0)
++          return DECODE_ERROR;
++      else
++          val += pos(token[i]);
++    }
++    if (marker > 2)
++      return DECODE_ERROR;
++    return (marker << 24) | val;
++}
++
++ssize_t
++base64Decode(const char *str, void *data)
++{
++    const char *p;
++    unsigned char *q;
++
++    q = data;
++    p = str;
++
++    while (*p && *p && (*p == '=' || strchr(base64_chars, *p))) {
++      unsigned int val = token_decode(p);
++      unsigned int marker = (val >> 24) & 0xff;
++      if (val == DECODE_ERROR)
++          return -1;
++      *q++ = (val >> 16) & 0xff;
++      if (marker < 2)
++          *q++ = (val >> 8) & 0xff;
++      if (marker < 1)
++          *q++ = val & 0xff;
++      p += 4;
++      if (*p == '\n')
++          p++;
++    }
++    return q - (unsigned char *) data;
++}
++
++int
++base64Valid(const char *str)
++{
++    const char *p = str;
++    int valid = 1;
++
++    while (*p && *p && (*p == '=' || strchr(base64_chars, *p))) {
++      unsigned int val = token_decode(p);
++      if (val == DECODE_ERROR) {
++            valid = 0;
++          break;
++        }
++      p += 4;
++      if (*p == '\n')
++          p++;
++    }
++    return valid;
++}
+diff --git a/mech_eap/util_base64.h b/mech_eap/util_base64.h
+new file mode 100644
+index 0000000..d015efe
+--- /dev/null
++++ b/mech_eap/util_base64.h
+@@ -0,0 +1,58 @@
++/*
++ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
++ * (Royal Institute of Technology, Stockholm, Sweden).
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of the Institute nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/* $Id$ */
++
++#ifndef _UTIL_BASE64_H_
++#define _UTIL_BASE64_H_
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++ssize_t
++base64Encode(const void *, int, char **);
++
++ssize_t
++base64Decode(const char *, void *);
++
++int
++base64Valid(const char *str);
++
++#define BASE64_EXPAND(n)        (n * 4 / 3 + 4)
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/mech_eap/util_buffer.c b/mech_eap/util_buffer.c
+new file mode 100644
+index 0000000..e135db9
+--- /dev/null
++++ b/mech_eap/util_buffer.c
+@@ -0,0 +1,103 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Buffer handling helpers.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++makeStringBuffer(OM_uint32 *minor,
++                 const char *string,
++                 gss_buffer_t buffer)
++{
++    size_t len = strlen(string);
++
++    buffer->value = GSSEAP_MALLOC(len + 1);
++    if (buffer->value == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++    memcpy(buffer->value, string, len + 1);
++    buffer->length = len;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++bufferToString(OM_uint32 *minor,
++               const gss_buffer_t buffer,
++               char **pString)
++{
++    char *s;
++
++    s = GSSEAP_MALLOC(buffer->length + 1);
++    if (s == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++    memcpy(s, buffer->value, buffer->length);
++    s[buffer->length] = '\0';
++
++    *pString = s;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++duplicateBuffer(OM_uint32 *minor,
++                const gss_buffer_t src,
++                gss_buffer_t dst)
++{
++    dst->length = 0;
++    dst->value = NULL;
++
++    if (src == GSS_C_NO_BUFFER)
++        return GSS_S_COMPLETE;
++
++    dst->value = GSSEAP_MALLOC(src->length + 1);
++    if (dst->value == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    dst->length = src->length;
++    memcpy(dst->value, src->value, dst->length);
++
++    ((unsigned char *)dst->value)[dst->length] = '\0';
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/util_cksum.c b/mech_eap/util_cksum.c
+new file mode 100644
+index 0000000..aedc93e
+--- /dev/null
++++ b/mech_eap/util_cksum.c
+@@ -0,0 +1,242 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 1993 by OpenVision Technologies, Inc.
++ *
++ * Permission to use, copy, modify, distribute, and sell this software
++ * and its documentation for any purpose is hereby granted without fee,
++ * provided that the above copyright notice appears in all copies and
++ * that both that copyright notice and this permission notice appear in
++ * supporting documentation, and that the name of OpenVision not be used
++ * in advertising or publicity pertaining to distribution of the software
++ * without specific, written prior permission. OpenVision makes no
++ * representations about the suitability of this software for any
++ * purpose.  It is provided "as is" without express or implied warranty.
++ *
++ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
++ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
++ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++ * PERFORMANCE OF THIS SOFTWARE.
++ */
++
++/*
++ * Message protection services: checksum helpers.
++ */
++
++#include "gssapiP_eap.h"
++
++static int
++gssEapChecksum(krb5_context context,
++               krb5_cksumtype type,
++               size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++               krb5_crypto crypto,
++#else
++               krb5_keyblock *crypto,
++#endif
++               krb5_keyusage sign_usage,
++               gss_iov_buffer_desc *iov,
++               int iov_count,
++               int verify,
++               int *valid)
++{
++    krb5_error_code code;
++    gss_iov_buffer_desc *header;
++    gss_iov_buffer_desc *trailer;
++    krb5_crypto_iov *kiov;
++    size_t kiov_count;
++    int i = 0, j;
++    size_t k5_checksumlen;
++
++    if (verify)
++        *valid = FALSE;
++
++    code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_CHECKSUM, &k5_checksumlen);
++    if (code != 0)
++        return code;
++
++    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
++    GSSEAP_ASSERT(header != NULL);
++
++    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
++    GSSEAP_ASSERT(rrc != 0 || trailer != NULL);
++
++    if (trailer == NULL) {
++        if (rrc != k5_checksumlen)
++            return KRB5_BAD_MSIZE;
++        if (header->buffer.length != 16 + k5_checksumlen)
++            return KRB5_BAD_MSIZE;
++    } else if (trailer->buffer.length != k5_checksumlen)
++        return KRB5_BAD_MSIZE;
++
++    kiov_count = 2 + iov_count;
++    kiov = (krb5_crypto_iov *)GSSEAP_MALLOC(kiov_count * sizeof(krb5_crypto_iov));
++    if (kiov == NULL)
++        return ENOMEM;
++
++    /* Checksum over ( Data | Header ) */
++
++    /* Data */
++    for (j = 0; j < iov_count; j++) {
++        kiov[i].flags = gssEapMapCryptoFlag(iov[j].type);
++        kiov[i].data.length = iov[j].buffer.length;
++        kiov[i].data.data = (char *)iov[j].buffer.value;
++        i++;
++    }
++
++    /* Header */
++    kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
++    kiov[i].data.length = 16;
++    kiov[i].data.data = (char *)header->buffer.value;
++    i++;
++
++    /* Checksum */
++    kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
++    if (trailer == NULL) {
++        kiov[i].data.length = header->buffer.length - 16;
++        kiov[i].data.data = (char *)header->buffer.value + 16;
++    } else {
++        kiov[i].data.length = trailer->buffer.length;
++        kiov[i].data.data = (char *)trailer->buffer.value;
++    }
++    i++;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    if (verify) {
++        code = krb5_verify_checksum_iov(context, crypto, sign_usage,
++                                        kiov, kiov_count, &type);
++        *valid = (code == 0);
++    } else {
++        code = krb5_create_checksum_iov(context, crypto, sign_usage,
++                                        kiov, kiov_count, &type);
++    }
++#else
++    if (verify) {
++        krb5_boolean kvalid = FALSE;
++
++        code = krb5_c_verify_checksum_iov(context, type, crypto,
++                                          sign_usage, kiov, kiov_count, &kvalid);
++
++        *valid = kvalid;
++    } else {
++        code = krb5_c_make_checksum_iov(context, type, crypto,
++                                        sign_usage, kiov, kiov_count);
++    }
++#endif /* HAVE_HEIMDAL_VERSION */
++
++    GSSEAP_FREE(kiov);
++
++    return code;
++}
++
++int
++gssEapSign(krb5_context context,
++           krb5_cksumtype type,
++           size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++           krb5_crypto crypto,
++#else
++           krb5_keyblock *crypto,
++#endif
++           krb5_keyusage sign_usage,
++           gss_iov_buffer_desc *iov,
++           int iov_count)
++{
++    return gssEapChecksum(context, type, rrc, crypto,
++                          sign_usage, iov, iov_count, 0, NULL);
++}
++
++int
++gssEapVerify(krb5_context context,
++             krb5_cksumtype type,
++             size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++             krb5_crypto crypto,
++#else
++             krb5_keyblock *crypto,
++#endif
++             krb5_keyusage sign_usage,
++             gss_iov_buffer_desc *iov,
++             int iov_count,
++             int *valid)
++{
++    return gssEapChecksum(context, type, rrc, crypto,
++                          sign_usage, iov, iov_count, 1, valid);
++}
++
++#if 0
++OM_uint32
++gssEapEncodeGssChannelBindings(OM_uint32 *minor,
++                               gss_channel_bindings_t chanBindings,
++                               gss_buffer_t encodedBindings)
++{
++    OM_uint32 major, tmpMinor;
++    size_t length;
++    unsigned char *p;
++
++    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) {
++        length = 24;
++        length += chanBindings->initiator_address.length;
++        length += chanBindings->acceptor_address.length;
++        length += chanBindings->application_data.length;
++
++        encodedBindings->value = GSSEAP_MALLOC(length);
++        if (encodedBindings->value == NULL) {
++            *minor = ENOMEM;
++            return GSS_S_FAILURE;
++        }
++
++        encodedBindings->length = length;
++        p = (unsigned char *)encodedBindings->value;
++
++        store_uint32_be(chanBindings->initiator_addrtype, p);
++        store_buffer(&chanBindings->initiator_address, p + 4, 0);
++        p += 4 + chanBindings->initiator_address.length;
++
++        store_uint32_be(chanBindings->acceptor_addrtype, p);
++        store_buffer(&chanBindings->acceptor_address, p + 4, 0);
++        p += 4 + chanBindings->acceptor_address.length;
++
++        store_buffer(&chanBindings->application_data, p, 1);
++        p += chanBindings->application_data.length;
++    } else {
++        encodedBindings->length = 0;
++        encodedBindings->value = NULL;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++#endif
+diff --git a/mech_eap/util_context.c b/mech_eap/util_context.c
+new file mode 100644
+index 0000000..e18edc5
+--- /dev/null
++++ b/mech_eap/util_context.c
+@@ -0,0 +1,377 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Utility routines for context handles.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++gssEapAllocContext(OM_uint32 *minor,
++                   gss_ctx_id_t *pCtx)
++{
++    OM_uint32 tmpMinor;
++    gss_ctx_id_t ctx;
++
++    GSSEAP_ASSERT(*pCtx == GSS_C_NO_CONTEXT);
++
++    ctx = (gss_ctx_id_t)GSSEAP_CALLOC(1, sizeof(*ctx));
++    if (ctx == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    if (GSSEAP_MUTEX_INIT(&ctx->mutex) != 0) {
++        *minor = GSSEAP_GET_LAST_ERROR();
++        gssEapReleaseContext(&tmpMinor, &ctx);
++        return GSS_S_FAILURE;
++    }
++
++    ctx->state = GSSEAP_STATE_INITIAL;
++    ctx->mechanismUsed = GSS_C_NO_OID;
++
++    /*
++     * Integrity, confidentiality, sequencing and replay detection are
++     * always available.  Regardless of what flags are requested in
++     * GSS_Init_sec_context, implementations MUST set the flag corresponding
++     * to these services in the output of GSS_Init_sec_context and
++     * GSS_Accept_sec_context.
++    */
++    ctx->gssFlags = GSS_C_TRANS_FLAG    |   /* exporting contexts */
++                    GSS_C_INTEG_FLAG    |   /* integrity */
++                    GSS_C_CONF_FLAG     |   /* confidentiality */
++                    GSS_C_SEQUENCE_FLAG |   /* sequencing */
++                    GSS_C_REPLAY_FLAG;      /* replay detection */
++
++    *pCtx = ctx;
++
++    return GSS_S_COMPLETE;
++}
++
++static void
++releaseInitiatorContext(struct gss_eap_initiator_ctx *ctx)
++{
++    eap_peer_sm_deinit(ctx->eap);
++}
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++static void
++releaseAcceptorContext(struct gss_eap_acceptor_ctx *ctx)
++{
++    OM_uint32 tmpMinor;
++
++    if (ctx->radConn != NULL)
++        rs_conn_destroy(ctx->radConn);
++    if (ctx->radContext != NULL)
++        rs_context_destroy(ctx->radContext);
++    if (ctx->radServer != NULL)
++        GSSEAP_FREE(ctx->radServer);
++    gss_release_buffer(&tmpMinor, &ctx->state);
++    if (ctx->vps != NULL)
++        gssEapRadiusFreeAvps(&tmpMinor, &ctx->vps);
++}
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++
++OM_uint32
++gssEapReleaseContext(OM_uint32 *minor,
++                     gss_ctx_id_t *pCtx)
++{
++    OM_uint32 tmpMinor;
++    gss_ctx_id_t ctx = *pCtx;
++    krb5_context krbContext = NULL;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        return GSS_S_COMPLETE;
++    }
++
++    gssEapKerberosInit(&tmpMinor, &krbContext);
++
++#ifdef GSSEAP_ENABLE_REAUTH
++    if (ctx->flags & CTX_FLAG_KRB_REAUTH) {
++        gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
++    } else
++#endif /* GSSEAP_ENABLE_REAUTH */
++    if (CTX_IS_INITIATOR(ctx)) {
++        releaseInitiatorContext(&ctx->initiatorCtx);
++    }
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    else {
++        releaseAcceptorContext(&ctx->acceptorCtx);
++    }
++#endif /* GSSEAP_ENABLE_ACCEPTOR */
++
++    krb5_free_keyblock_contents(krbContext, &ctx->rfc3961Key);
++    gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
++    gssEapReleaseName(&tmpMinor, &ctx->acceptorName);
++    gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed);
++    sequenceFree(&tmpMinor, &ctx->seqState);
++    gssEapReleaseCred(&tmpMinor, &ctx->cred);
++
++    GSSEAP_MUTEX_DESTROY(&ctx->mutex);
++
++    memset(ctx, 0, sizeof(*ctx));
++    GSSEAP_FREE(ctx);
++    *pCtx = GSS_C_NO_CONTEXT;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapMakeToken(OM_uint32 *minor,
++                gss_ctx_id_t ctx,
++                const gss_buffer_t innerToken,
++                enum gss_eap_token_type tokenType,
++                gss_buffer_t outputToken)
++{
++    unsigned char *p;
++
++    GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
++
++    outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length);
++    outputToken->value = GSSEAP_MALLOC(outputToken->length);
++    if (outputToken->value == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    p = (unsigned char *)outputToken->value;
++    makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType);
++    memcpy(p, innerToken->value, innerToken->length);
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapVerifyToken(OM_uint32 *minor,
++                  gss_ctx_id_t ctx,
++                  const gss_buffer_t inputToken,
++                  enum gss_eap_token_type *actualToken,
++                  gss_buffer_t innerInputToken)
++{
++    OM_uint32 major;
++    size_t bodySize;
++    unsigned char *p = (unsigned char *)inputToken->value;
++    gss_OID_desc oidBuf;
++    gss_OID oid;
++
++    if (ctx->mechanismUsed != GSS_C_NO_OID) {
++        oid = ctx->mechanismUsed;
++    } else {
++        oidBuf.elements = NULL;
++        oidBuf.length = 0;
++        oid = &oidBuf;
++    }
++
++    major = verifyTokenHeader(minor, oid, &bodySize, &p,
++                              inputToken->length, actualToken);
++    if (GSS_ERROR(major))
++        return major;
++
++    if (ctx->mechanismUsed == GSS_C_NO_OID) {
++        major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed);
++        if (GSS_ERROR(major))
++            return major;
++    }
++
++    innerInputToken->length = bodySize;
++    innerInputToken->value = p;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapContextTime(OM_uint32 *minor,
++                  gss_ctx_id_t context_handle,
++                  OM_uint32 *time_rec)
++{
++    *minor = 0;
++
++    if (context_handle->expiryTime == 0) {
++        *time_rec = GSS_C_INDEFINITE;
++    } else {
++        time_t now, lifetime;
++
++        time(&now);
++        lifetime = context_handle->expiryTime - now;
++        if (lifetime <= 0) {
++            *time_rec = 0;
++            return GSS_S_CONTEXT_EXPIRED;
++        }
++        *time_rec = lifetime;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
++                           gss_ctx_id_t ctx,
++                           gss_buffer_t tokenMIC,
++                           int verifyMIC)
++{
++    OM_uint32 major;
++    gss_iov_buffer_desc *iov = NULL;
++    size_t i = 0, j;
++    enum gss_eap_token_type tokType;
++    OM_uint32 micTokType;
++    unsigned char wireTokType[2];
++    unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
++    const struct gss_eap_token_buffer_set *tokens;
++
++    tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
++
++    GSSEAP_ASSERT(tokens != NULL);
++
++    iov = GSSEAP_CALLOC(2 + (3 * tokens->buffers.count) + 1, sizeof(*iov));
++    if (iov == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++
++    innerTokTypes = GSSEAP_MALLOC(4 * tokens->buffers.count);
++    if (innerTokTypes == NULL) {
++        *minor = ENOMEM;
++        major = GSS_S_FAILURE;
++        goto cleanup;
++    }
++
++    innerTokLengths = GSSEAP_MALLOC(4 * tokens->buffers.count);
++    if (innerTokLengths == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++
++    /* Mechanism OID */
++    GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
++    iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
++    iov[i].buffer.length = ctx->mechanismUsed->length;
++    iov[i].buffer.value = ctx->mechanismUsed->elements;
++    i++;
++
++    /* Token type */
++    if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
++        tokType = TOK_TYPE_INITIATOR_CONTEXT;
++        micTokType = ITOK_TYPE_INITIATOR_MIC;
++    } else {
++        tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
++        micTokType = ITOK_TYPE_ACCEPTOR_MIC;
++    }
++    store_uint16_be(tokType, wireTokType);
++
++    iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
++    iov[i].buffer.length = sizeof(wireTokType);
++    iov[i].buffer.value = wireTokType;
++    i++;
++
++    for (j = 0; j < tokens->buffers.count; j++) {
++        if (verifyMIC &&
++            (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
++            continue; /* will use this slot for trailer */
++
++        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
++        iov[i].buffer.length = 4;
++        iov[i].buffer.value = &innerTokTypes[j * 4];
++        store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
++                        iov[i].buffer.value);
++        i++;
++
++        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
++        iov[i].buffer.length = 4;
++        iov[i].buffer.value = &innerTokLengths[j * 4];
++        store_uint32_be(tokens->buffers.elements[j].length,
++                        iov[i].buffer.value);
++        i++;
++
++        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
++        iov[i].buffer = tokens->buffers.elements[j];
++        i++;
++    }
++
++    if (verifyMIC) {
++        GSSEAP_ASSERT(tokenMIC->length >= 16);
++
++        GSSEAP_ASSERT(i < 2 + (3 * tokens->buffers.count));
++
++        iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
++        iov[i].buffer = *tokenMIC;
++        i++;
++
++        major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
++                                        iov, i, TOK_TYPE_MIC);
++    } else {
++        iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
++        major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
++                                   iov, i, TOK_TYPE_MIC);
++        if (!GSS_ERROR(major))
++            *tokenMIC = iov[i - 1].buffer;
++    }
++
++cleanup:
++    if (iov != NULL)
++        gssEapReleaseIov(iov, tokens->buffers.count);
++    if (innerTokTypes != NULL)
++        GSSEAP_FREE(innerTokTypes);
++    if (innerTokLengths != NULL)
++        GSSEAP_FREE(innerTokLengths);
++
++    return major;
++}
++
++OM_uint32
++gssEapMakeTokenMIC(OM_uint32 *minor,
++                   gss_ctx_id_t ctx,
++                   gss_buffer_t tokenMIC)
++{
++    tokenMIC->length = 0;
++    tokenMIC->value = NULL;
++
++    return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, FALSE);
++}
++
++OM_uint32
++gssEapVerifyTokenMIC(OM_uint32 *minor,
++                     gss_ctx_id_t ctx,
++                     const gss_buffer_t tokenMIC)
++{
++    if (tokenMIC->length < 16) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_BAD_SIG;
++    }
++
++    return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
++}
+diff --git a/mech_eap/util_cred.c b/mech_eap/util_cred.c
+new file mode 100644
+index 0000000..746bd61
+--- /dev/null
++++ b/mech_eap/util_cred.c
+@@ -0,0 +1,756 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Utility routines for credential handles.
++ */
++
++#include "gssapiP_eap.h"
++
++#ifdef WIN32
++# include <shlobj.h>     /* may need to use ShFolder.h instead */
++# include <stdio.h>
++#else
++# include <pwd.h>
++#endif
++
++OM_uint32
++gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
++{
++    OM_uint32 tmpMinor;
++    gss_cred_id_t cred;
++
++    *pCred = GSS_C_NO_CREDENTIAL;
++
++    cred = (gss_cred_id_t)GSSEAP_CALLOC(1, sizeof(*cred));
++    if (cred == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) {
++        *minor = GSSEAP_GET_LAST_ERROR();
++        gssEapReleaseCred(&tmpMinor, &cred);
++        return GSS_S_FAILURE;
++    }
++
++    *pCred = cred;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static void
++zeroAndReleasePassword(gss_buffer_t password)
++{
++    if (password->value != NULL) {
++        memset(password->value, 0, password->length);
++        GSSEAP_FREE(password->value);
++    }
++
++    password->value = NULL;
++    password->length = 0;
++}
++
++OM_uint32
++gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
++{
++    OM_uint32 tmpMinor;
++    gss_cred_id_t cred = *pCred;
++    krb5_context krbContext = NULL;
++
++    if (cred == GSS_C_NO_CREDENTIAL) {
++        return GSS_S_COMPLETE;
++    }
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    gssEapReleaseName(&tmpMinor, &cred->name);
++    gssEapReleaseName(&tmpMinor, &cred->target);
++
++    zeroAndReleasePassword(&cred->password);
++
++    gss_release_buffer(&tmpMinor, &cred->radiusConfigFile);
++    gss_release_buffer(&tmpMinor, &cred->radiusConfigStanza);
++    gss_release_buffer(&tmpMinor, &cred->caCertificate);
++    gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
++    gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
++
++#ifdef GSSEAP_ENABLE_REAUTH
++    if (cred->krbCredCache != NULL) {
++        if (cred->flags & CRED_FLAG_DEFAULT_CCACHE)
++            krb5_cc_close(krbContext, cred->krbCredCache);
++        else
++            krb5_cc_destroy(krbContext, cred->krbCredCache);
++    }
++    if (cred->reauthCred != GSS_C_NO_CREDENTIAL)
++        gssReleaseCred(&tmpMinor, &cred->reauthCred);
++#endif
++
++    GSSEAP_MUTEX_DESTROY(&cred->mutex);
++    memset(cred, 0, sizeof(*cred));
++    GSSEAP_FREE(cred);
++    *pCred = NULL;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++readStaticIdentityFile(OM_uint32 *minor,
++                       gss_buffer_t defaultIdentity,
++                       gss_buffer_t defaultPassword)
++{
++    OM_uint32 major, tmpMinor;
++    FILE *fp = NULL;
++    char buf[BUFSIZ];
++    char *ccacheName;
++    int i = 0;
++#ifndef WIN32
++    struct passwd *pw = NULL, pwd;
++    char pwbuf[BUFSIZ];
++#endif
++
++    defaultIdentity->length = 0;
++    defaultIdentity->value = NULL;
++
++    if (defaultPassword != GSS_C_NO_BUFFER) {
++        defaultPassword->length = 0;
++        defaultPassword->value = NULL;
++    }
++
++    ccacheName = getenv("GSSEAP_IDENTITY");
++    if (ccacheName == NULL) {
++#ifdef WIN32
++        TCHAR szPath[MAX_PATH];
++
++        if (!SUCCEEDED(SHGetFolderPath(NULL,
++                                       CSIDL_APPDATA, /* |CSIDL_FLAG_CREATE */
++                                       NULL, /* User access token */
++                                       0,    /* SHGFP_TYPE_CURRENT */
++                                       szPath))) {
++            major = GSS_S_CRED_UNAVAIL;
++            *minor = GSSEAP_GET_LAST_ERROR(); /* XXX */
++            goto cleanup;
++        }
++
++        snprintf(buf, sizeof(buf), "%s/.gss_eap_id", szPath);
++#else
++        if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 ||
++            pw == NULL || pw->pw_dir == NULL) {
++            major = GSS_S_CRED_UNAVAIL;
++            *minor = GSSEAP_GET_LAST_ERROR();
++            goto cleanup;
++        }
++
++        snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir);
++#endif /* WIN32 */
++        ccacheName = buf;
++    }
++
++    fp = fopen(ccacheName, "r");
++    if (fp == NULL) {
++        major = GSS_S_CRED_UNAVAIL;
++        *minor = GSSEAP_NO_DEFAULT_CRED;
++        goto cleanup;
++    }
++
++    while (fgets(buf, sizeof(buf), fp) != NULL) {
++        gss_buffer_desc src, *dst;
++
++        src.length = strlen(buf);
++        src.value = buf;
++
++        if (src.length == 0)
++            break;
++
++        if (buf[src.length - 1] == '\n') {
++            buf[src.length - 1] = '\0';
++            if (--src.length == 0)
++                break;
++        }
++
++        if (i == 0)
++            dst = defaultIdentity;
++        else if (i == 1)
++            dst = defaultPassword;
++        else
++            break;
++
++        if (dst != GSS_C_NO_BUFFER) {
++            major = duplicateBuffer(minor, &src, dst);
++            if (GSS_ERROR(major))
++                goto cleanup;
++        }
++
++        i++;
++    }
++
++    if (defaultIdentity->length == 0) {
++        major = GSS_S_CRED_UNAVAIL;
++        *minor = GSSEAP_NO_DEFAULT_CRED;
++        goto cleanup;
++    }
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (fp != NULL)
++        fclose(fp);
++
++    if (GSS_ERROR(major)) {
++        gss_release_buffer(&tmpMinor, defaultIdentity);
++        zeroAndReleasePassword(defaultPassword);
++    }
++
++    memset(buf, 0, sizeof(buf));
++
++    return major;
++}
++
++gss_OID
++gssEapPrimaryMechForCred(gss_cred_id_t cred)
++{
++    gss_OID nameMech = GSS_C_NO_OID;
++
++    if (cred->mechanisms != GSS_C_NO_OID_SET &&
++        cred->mechanisms->count == 1)
++        nameMech = &cred->mechanisms->elements[0];
++
++    return nameMech;
++}
++
++OM_uint32
++gssEapAcquireCred(OM_uint32 *minor,
++                  const gss_name_t desiredName,
++                  OM_uint32 timeReq GSSEAP_UNUSED,
++                  const gss_OID_set desiredMechs,
++                  int credUsage,
++                  gss_cred_id_t *pCred,
++                  gss_OID_set *pActualMechs,
++                  OM_uint32 *timeRec)
++{
++    OM_uint32 major, tmpMinor;
++    gss_cred_id_t cred;
++
++    /* XXX TODO validate with changed set_cred_option API */
++    *pCred = GSS_C_NO_CREDENTIAL;
++
++    major = gssEapAllocCred(minor, &cred);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    switch (credUsage) {
++    case GSS_C_BOTH:
++        cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT;
++        break;
++    case GSS_C_INITIATE:
++        cred->flags |= CRED_FLAG_INITIATE;
++        break;
++    case GSS_C_ACCEPT:
++        cred->flags |= CRED_FLAG_ACCEPT;
++        break;
++    default:
++        major = GSS_S_FAILURE;
++        *minor = GSSEAP_BAD_USAGE;
++        goto cleanup;
++        break;
++    }
++
++    major = gssEapValidateMechs(minor, desiredMechs);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (desiredName != GSS_C_NO_NAME) {
++        GSSEAP_MUTEX_LOCK(&desiredName->mutex);
++
++        major = gssEapDuplicateName(minor, desiredName, &cred->name);
++        if (GSS_ERROR(major)) {
++            GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
++            goto cleanup;
++        }
++
++        GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
++    }
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    if (cred->flags & CRED_FLAG_ACCEPT) {
++        struct rs_context *radContext;
++
++        major = gssEapCreateRadiusContext(minor, cred, &radContext);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        rs_context_destroy(radContext);
++    }
++#endif
++
++    if (pActualMechs != NULL) {
++        major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (timeRec != NULL)
++        *timeRec = GSS_C_INDEFINITE;
++
++    *pCred = cred;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gssEapReleaseCred(&tmpMinor, &cred);
++
++    return major;
++}
++
++/*
++ * Return TRUE if cred available for mechanism. Caller need no acquire
++ * lock because mechanisms list is immutable.
++ */
++int
++gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
++{
++    OM_uint32 minor;
++    int present = 0;
++
++    GSSEAP_ASSERT(mech != GSS_C_NO_OID);
++
++    if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
++        return TRUE;
++
++    gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
++
++    return present;
++}
++
++static OM_uint32
++staticIdentityFileResolveDefaultIdentity(OM_uint32 *minor,
++                                         const gss_cred_id_t cred,
++                                         gss_name_t *pName)
++{
++    OM_uint32 major, tmpMinor;
++    gss_OID nameMech = gssEapPrimaryMechForCred(cred);
++    gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
++
++    *pName = GSS_C_NO_NAME;
++
++    major = readStaticIdentityFile(minor, &defaultIdentity, GSS_C_NO_BUFFER);
++    if (major == GSS_S_COMPLETE) {
++        major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
++                                 nameMech, pName);
++    }
++
++    gss_release_buffer(&tmpMinor, &defaultIdentity);
++
++    return major;
++}
++
++static OM_uint32
++gssEapResolveCredIdentity(OM_uint32 *minor,
++                          gss_cred_id_t cred)
++{
++    OM_uint32 major;
++    gss_OID nameMech = gssEapPrimaryMechForCred(cred);
++
++    if (cred->name != GSS_C_NO_NAME) {
++        *minor = 0;
++        return GSS_S_COMPLETE;
++    }
++
++    if (cred->flags & CRED_FLAG_ACCEPT) {
++        gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
++        char serviceName[5 + MAXHOSTNAMELEN];
++
++        /* default host-based service is host@localhost */
++        memcpy(serviceName, "host@", 5);
++        if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
++            *minor = GSSEAP_NO_HOSTNAME;
++            return GSS_S_FAILURE;
++        }
++
++        nameBuf.value = serviceName;
++        nameBuf.length = strlen((char *)nameBuf.value);
++
++        major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
++                                 nameMech, &cred->name);
++        if (GSS_ERROR(major))
++            return major;
++    } else if (cred->flags & CRED_FLAG_INITIATE) {
++#ifdef HAVE_MOONSHOT_GET_IDENTITY
++        major = libMoonshotResolveDefaultIdentity(minor, cred, &cred->name);
++        if (major == GSS_S_CRED_UNAVAIL)
++#endif
++            major = staticIdentityFileResolveDefaultIdentity(minor, cred, &cred->name);
++        if (major != GSS_S_CRED_UNAVAIL)
++            return major;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapInquireCred(OM_uint32 *minor,
++                  gss_cred_id_t cred,
++                  gss_name_t *name,
++                  OM_uint32 *pLifetime,
++                  gss_cred_usage_t *cred_usage,
++                  gss_OID_set *mechanisms)
++{
++    OM_uint32 major;
++    time_t now, lifetime;
++
++    if (name != NULL) {
++        major = gssEapResolveCredIdentity(minor, cred);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        if (cred->name != GSS_C_NO_NAME) {
++            major = gssEapDuplicateName(minor, cred->name, name);
++            if (GSS_ERROR(major))
++                goto cleanup;
++        } else
++            *name = GSS_C_NO_NAME;
++    }
++
++    if (cred_usage != NULL) {
++        OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT));
++
++        switch (flags) {
++        case CRED_FLAG_INITIATE:
++            *cred_usage = GSS_C_INITIATE;
++            break;
++        case CRED_FLAG_ACCEPT:
++            *cred_usage = GSS_C_ACCEPT;
++            break;
++        default:
++            *cred_usage = GSS_C_BOTH;
++            break;
++        }
++    }
++
++    if (mechanisms != NULL) {
++        if (cred->mechanisms != GSS_C_NO_OID_SET)
++            major = duplicateOidSet(minor, cred->mechanisms, mechanisms);
++        else
++            major = gssEapIndicateMechs(minor, mechanisms);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (cred->expiryTime == 0) {
++        lifetime = GSS_C_INDEFINITE;
++    } else  {
++        now = time(NULL);
++        lifetime = now - cred->expiryTime;
++        if (lifetime < 0)
++            lifetime = 0;
++    }
++
++    if (pLifetime != NULL) {
++        *pLifetime = lifetime;
++    }
++
++    if (lifetime == 0) {
++        major = GSS_S_CREDENTIALS_EXPIRED;
++        *minor = GSSEAP_CRED_EXPIRED;
++        goto cleanup;
++    }
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    return major;
++}
++
++OM_uint32
++gssEapSetCredPassword(OM_uint32 *minor,
++                      gss_cred_id_t cred,
++                      const gss_buffer_t password)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc newPassword = GSS_C_EMPTY_BUFFER;
++
++    if (cred->flags & CRED_FLAG_RESOLVED) {
++        major = GSS_S_FAILURE;
++        *minor = GSSEAP_CRED_RESOLVED;
++        goto cleanup;
++    }
++
++    if (password != GSS_C_NO_BUFFER) {
++        major = duplicateBuffer(minor, password, &newPassword);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        cred->flags |= CRED_FLAG_PASSWORD;
++    } else {
++        cred->flags &= ~(CRED_FLAG_PASSWORD);
++    }
++
++    gss_release_buffer(&tmpMinor, &cred->password);
++    cred->password = newPassword;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    return major;
++}
++
++OM_uint32
++gssEapSetCredService(OM_uint32 *minor,
++                     gss_cred_id_t cred,
++                     const gss_name_t target)
++{
++    OM_uint32 major, tmpMinor;
++    gss_name_t newTarget = GSS_C_NO_NAME;
++
++    if (cred->flags & CRED_FLAG_RESOLVED) {
++        major = GSS_S_FAILURE;
++        *minor = GSSEAP_CRED_RESOLVED;
++        goto cleanup;
++    }
++
++    if (target != GSS_C_NO_NAME) {
++        major = gssEapDuplicateName(minor, target, &newTarget);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        cred->flags |= CRED_FLAG_TARGET;
++    } else {
++        cred->flags &= ~(CRED_FLAG_TARGET);
++    }
++
++    gssEapReleaseName(&tmpMinor, &cred->target);
++    cred->target = newTarget;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    return major;
++}
++
++static OM_uint32
++gssEapDuplicateCred(OM_uint32 *minor,
++                    const gss_cred_id_t src,
++                    gss_cred_id_t *pDst)
++{
++    OM_uint32 major, tmpMinor;
++    gss_cred_id_t dst = GSS_C_NO_CREDENTIAL;
++
++    *pDst = GSS_C_NO_CREDENTIAL;
++
++    major = gssEapAllocCred(minor, &dst);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    dst->flags = src->flags;
++
++    if (src->name != GSS_C_NO_NAME) {
++        major = gssEapDuplicateName(minor, src->name, &dst->name);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (src->target != GSS_C_NO_NAME) {
++        major = gssEapDuplicateName(minor, src->target, &dst->target);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (src->password.value != NULL) {
++        major = duplicateBuffer(minor, &src->password, &dst->password);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    major = duplicateOidSet(minor, src->mechanisms, &dst->mechanisms);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    dst->expiryTime = src->expiryTime;
++
++    if (src->radiusConfigFile.value != NULL)
++        duplicateBufferOrCleanup(&src->radiusConfigFile, &dst->radiusConfigFile);
++    if (src->radiusConfigStanza.value != NULL)
++        duplicateBufferOrCleanup(&src->radiusConfigStanza, &dst->radiusConfigStanza);
++    if (src->caCertificate.value != NULL)
++        duplicateBufferOrCleanup(&src->caCertificate, &dst->caCertificate);
++    if (src->subjectNameConstraint.value != NULL)
++        duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
++    if (src->subjectAltNameConstraint.value != NULL)
++        duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
++
++#ifdef GSSEAP_ENABLE_REAUTH
++    /* XXX krbCredCache, reauthCred */
++#endif
++
++    *pDst = dst;
++    dst = GSS_C_NO_CREDENTIAL;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    gssEapReleaseCred(&tmpMinor, &dst);
++
++    return major;
++}
++
++static OM_uint32
++staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
++    gss_name_t defaultIdentityName = GSS_C_NO_NAME;
++    gss_buffer_desc defaultPassword = GSS_C_EMPTY_BUFFER;
++    int isDefaultIdentity = FALSE;
++
++    major = readStaticIdentityFile(minor, &defaultIdentity, &defaultPassword);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
++                             gssEapPrimaryMechForCred(cred), &defaultIdentityName);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (defaultIdentityName == GSS_C_NO_NAME) {
++        if (cred->name == GSS_C_NO_NAME) {
++            major = GSS_S_CRED_UNAVAIL;
++            *minor = GSSEAP_NO_DEFAULT_IDENTITY;
++            goto cleanup;
++        }
++    } else {
++        if (cred->name == GSS_C_NO_NAME) {
++            cred->name = defaultIdentityName;
++            defaultIdentityName = GSS_C_NO_NAME;
++            isDefaultIdentity = TRUE;
++        } else {
++            major = gssEapCompareName(minor, cred->name,
++                                      defaultIdentityName, &isDefaultIdentity);
++            if (GSS_ERROR(major))
++                goto cleanup;
++        }
++    }
++
++    if (isDefaultIdentity &&
++        (cred->flags & CRED_FLAG_PASSWORD) == 0) {
++        major = gssEapSetCredPassword(minor, cred, &defaultPassword);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++cleanup:
++    gssEapReleaseName(&tmpMinor, &defaultIdentityName);
++    zeroAndReleasePassword(&defaultPassword);
++    gss_release_buffer(&tmpMinor, &defaultIdentity);
++
++    return major;
++}
++
++OM_uint32
++gssEapResolveInitiatorCred(OM_uint32 *minor,
++                           const gss_cred_id_t cred,
++                           const gss_name_t targetName
++#ifndef HAVE_MOONSHOT_GET_IDENTITY
++                                                       GSSEAP_UNUSED
++#endif
++                           ,
++                           gss_cred_id_t *pResolvedCred)
++{
++    OM_uint32 major, tmpMinor;
++    gss_cred_id_t resolvedCred = GSS_C_NO_CREDENTIAL;
++
++    if (cred == GSS_C_NO_CREDENTIAL) {
++        major = gssEapAcquireCred(minor,
++                                  GSS_C_NO_NAME,
++                                  GSS_C_INDEFINITE,
++                                  GSS_C_NO_OID_SET,
++                                  GSS_C_INITIATE,
++                                  &resolvedCred,
++                                  NULL,
++                                  NULL);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    } else {
++        if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
++            major = GSS_S_NO_CRED;
++            *minor = GSSEAP_CRED_USAGE_MISMATCH;
++            goto cleanup;
++        }
++
++        major = gssEapDuplicateCred(minor, cred, &resolvedCred);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if ((resolvedCred->flags & CRED_FLAG_RESOLVED) == 0) {
++#ifdef HAVE_MOONSHOT_GET_IDENTITY
++        major = libMoonshotResolveInitiatorCred(minor, resolvedCred, targetName);
++        if (major == GSS_S_CRED_UNAVAIL)
++#endif
++            major = staticIdentityFileResolveInitiatorCred(minor, resolvedCred);
++        if (GSS_ERROR(major) && major != GSS_S_CRED_UNAVAIL)
++            goto cleanup;
++
++        /* If we have a caller-supplied password, the credential is resolved. */
++        if ((resolvedCred->flags & CRED_FLAG_PASSWORD) == 0) {
++            major = GSS_S_CRED_UNAVAIL;
++            *minor = GSSEAP_NO_DEFAULT_CRED;
++            goto cleanup;
++        }
++
++        resolvedCred->flags |= CRED_FLAG_RESOLVED;
++    }
++
++    *pResolvedCred = resolvedCred;
++    resolvedCred = GSS_C_NO_CREDENTIAL;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    gssEapReleaseCred(&tmpMinor, &resolvedCred);
++
++    return major;
++}
+diff --git a/mech_eap/util_crypt.c b/mech_eap/util_crypt.c
+new file mode 100644
+index 0000000..b6e203e
+--- /dev/null
++++ b/mech_eap/util_crypt.c
+@@ -0,0 +1,397 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 2001, 2008 by the Massachusetts Institute of Technology.
++ * Copyright 1993 by OpenVision Technologies, Inc.
++ *
++ * Permission to use, copy, modify, distribute, and sell this software
++ * and its documentation for any purpose is hereby granted without fee,
++ * provided that the above copyright notice appears in all copies and
++ * that both that copyright notice and this permission notice appear in
++ * supporting documentation, and that the name of OpenVision not be used
++ * in advertising or publicity pertaining to distribution of the software
++ * without specific, written prior permission. OpenVision makes no
++ * representations about the suitability of this software for any
++ * purpose.  It is provided "as is" without express or implied warranty.
++ *
++ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
++ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
++ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++ * PERFORMANCE OF THIS SOFTWARE.
++ */
++/*
++ * Copyright (C) 1998 by the FundsXpress, INC.
++ *
++ * All rights reserved.
++ *
++ * Export of this software from the United States of America may require
++ * a specific license from the United States Government.  It is the
++ * responsibility of any person or organization contemplating export to
++ * obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of FundsXpress. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  FundsXpress makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
++ */
++
++/*
++ * Message protection services: cryptography helpers.
++ */
++
++#include "gssapiP_eap.h"
++
++/*
++ * DCE_STYLE indicates actual RRC is EC + RRC
++ * EC is extra rotate count for DCE_STYLE, pad length otherwise
++ * RRC is rotate count.
++ */
++static krb5_error_code
++mapIov(krb5_context context, int dce_style, size_t ec, size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++       krb5_crypto crypto,
++#else
++       krb5_keyblock *crypto,
++#endif
++       gss_iov_buffer_desc *iov,
++       int iov_count, krb5_crypto_iov **pkiov,
++       size_t *pkiov_count)
++{
++    gss_iov_buffer_t header;
++    gss_iov_buffer_t trailer;
++    int i = 0, j;
++    size_t kiov_count;
++    krb5_crypto_iov *kiov;
++    size_t k5_headerlen = 0, k5_trailerlen = 0;
++    size_t gss_headerlen, gss_trailerlen;
++    krb5_error_code code;
++
++    *pkiov = NULL;
++    *pkiov_count = 0;
++
++    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
++    GSSEAP_ASSERT(header != NULL);
++
++    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
++    GSSEAP_ASSERT(trailer == NULL || rrc == 0);
++
++    code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
++    if (code != 0)
++        return code;
++
++    code = krbCryptoLength(context, crypto, KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
++    if (code != 0)
++        return code;
++
++    /* Check header and trailer sizes */
++    gss_headerlen = 16 /* GSS-Header */ + k5_headerlen; /* Kerb-Header */
++    gss_trailerlen = ec + 16 /* E(GSS-Header) */ + k5_trailerlen; /* Kerb-Trailer */
++
++    /* If we're caller without a trailer, we must rotate by trailer length */
++    if (trailer == NULL) {
++        size_t actual_rrc = rrc;
++
++        if (dce_style)
++            actual_rrc += ec; /* compensate for Windows bug */
++
++        if (actual_rrc != gss_trailerlen)
++            return KRB5_BAD_MSIZE;
++
++        gss_headerlen += gss_trailerlen;
++        gss_trailerlen = 0;
++    } else {
++        if (trailer->buffer.length != gss_trailerlen)
++            return KRB5_BAD_MSIZE;
++    }
++
++    if (header->buffer.length != gss_headerlen)
++        return KRB5_BAD_MSIZE;
++
++    kiov_count = 3 + iov_count;
++    kiov = (krb5_crypto_iov *)GSSEAP_MALLOC(kiov_count * sizeof(krb5_crypto_iov));
++    if (kiov == NULL)
++        return ENOMEM;
++
++    /*
++     * The krb5 header is located at the end of the GSS header.
++     */
++    kiov[i].flags = KRB5_CRYPTO_TYPE_HEADER;
++    kiov[i].data.length = k5_headerlen;
++    kiov[i].data.data = (char *)header->buffer.value + header->buffer.length - k5_headerlen;
++    i++;
++
++    for (j = 0; j < iov_count; j++) {
++        kiov[i].flags = gssEapMapCryptoFlag(iov[j].type);
++        if (kiov[i].flags == KRB5_CRYPTO_TYPE_EMPTY)
++            continue;
++
++        kiov[i].data.length = iov[j].buffer.length;
++        kiov[i].data.data = (char *)iov[j].buffer.value;
++        i++;
++    }
++
++    /*
++     * The EC and encrypted GSS header are placed in the trailer, which may
++     * be rotated directly after the plaintext header if no trailer buffer
++     * is provided.
++     */
++    kiov[i].flags = KRB5_CRYPTO_TYPE_DATA;
++    kiov[i].data.length = ec + 16; /* E(Header) */
++    if (trailer == NULL)
++        kiov[i].data.data = (char *)header->buffer.value + 16;
++    else
++        kiov[i].data.data = (char *)trailer->buffer.value;
++    i++;
++
++    /*
++     * The krb5 trailer is placed after the encrypted copy of the
++     * krb5 header (which may be in the GSS header or trailer).
++     */
++    kiov[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
++    kiov[i].data.length = k5_trailerlen;
++    kiov[i].data.data = (char *)kiov[i - 1].data.data + ec + 16; /* E(Header) */
++    i++;
++
++    *pkiov = kiov;
++    *pkiov_count = i;
++
++    return 0;
++}
++
++int
++gssEapEncrypt(krb5_context context,
++              int dce_style,
++              size_t ec,
++              size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++              krb5_crypto crypto,
++#else
++              krb5_keyblock *crypto,
++#endif
++              int usage,
++              gss_iov_buffer_desc *iov,
++              int iov_count)
++{
++    krb5_error_code code;
++    size_t kiov_count;
++    krb5_crypto_iov *kiov = NULL;
++
++    code = mapIov(context, dce_style, ec, rrc, crypto,
++                  iov, iov_count, &kiov, &kiov_count);
++    if (code != 0)
++        goto cleanup;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_encrypt_iov_ivec(context, crypto, usage, kiov, kiov_count, NULL);
++#else
++    code = krb5_c_encrypt_iov(context, crypto, usage, NULL, kiov, kiov_count);
++#endif
++    if (code != 0)
++        goto cleanup;
++
++cleanup:
++    if (kiov != NULL)
++        GSSEAP_FREE(kiov);
++
++    return code;
++}
++
++int
++gssEapDecrypt(krb5_context context,
++              int dce_style,
++              size_t ec,
++              size_t rrc,
++#ifdef HAVE_HEIMDAL_VERSION
++              krb5_crypto crypto,
++#else
++              krb5_keyblock *crypto,
++#endif
++              int usage,
++              gss_iov_buffer_desc *iov,
++              int iov_count)
++{
++    krb5_error_code code;
++    size_t kiov_count;
++    krb5_crypto_iov *kiov;
++
++    code = mapIov(context, dce_style, ec, rrc, crypto,
++                  iov, iov_count, &kiov, &kiov_count);
++    if (code != 0)
++        goto cleanup;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_decrypt_iov_ivec(context, crypto, usage, kiov, kiov_count, NULL);
++#else
++    code = krb5_c_decrypt_iov(context, crypto, usage, NULL, kiov, kiov_count);
++#endif
++
++cleanup:
++    if (kiov != NULL)
++        GSSEAP_FREE(kiov);
++
++    return code;
++}
++
++int
++gssEapMapCryptoFlag(OM_uint32 type)
++{
++    int ktype;
++
++    switch (GSS_IOV_BUFFER_TYPE(type)) {
++    case GSS_IOV_BUFFER_TYPE_DATA:
++    case GSS_IOV_BUFFER_TYPE_PADDING:
++        ktype = KRB5_CRYPTO_TYPE_DATA;
++        break;
++    case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
++        ktype = KRB5_CRYPTO_TYPE_SIGN_ONLY;
++        break;
++    default:
++        ktype = KRB5_CRYPTO_TYPE_EMPTY;
++        break;
++    }
++
++    return ktype;
++}
++
++gss_iov_buffer_t
++gssEapLocateIov(gss_iov_buffer_desc *iov, int iov_count, OM_uint32 type)
++{
++    int i;
++    gss_iov_buffer_t p = GSS_C_NO_IOV_BUFFER;
++
++    if (iov == GSS_C_NO_IOV_BUFFER)
++        return GSS_C_NO_IOV_BUFFER;
++
++    for (i = iov_count - 1; i >= 0; i--) {
++        if (GSS_IOV_BUFFER_TYPE(iov[i].type) == type) {
++            if (p == GSS_C_NO_IOV_BUFFER)
++                p = &iov[i];
++            else
++                return GSS_C_NO_IOV_BUFFER;
++        }
++    }
++
++    return p;
++}
++
++void
++gssEapIovMessageLength(gss_iov_buffer_desc *iov,
++                       int iov_count,
++                       size_t *data_length_p,
++                       size_t *assoc_data_length_p)
++{
++    int i;
++    size_t data_length = 0, assoc_data_length = 0;
++
++    GSSEAP_ASSERT(iov != GSS_C_NO_IOV_BUFFER);
++
++    *data_length_p = *assoc_data_length_p = 0;
++
++    for (i = 0; i < iov_count; i++) {
++        OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[i].type);
++
++        if (type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
++            assoc_data_length += iov[i].buffer.length;
++
++        if (type == GSS_IOV_BUFFER_TYPE_DATA ||
++            type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
++            data_length += iov[i].buffer.length;
++    }
++
++    *data_length_p = data_length;
++    *assoc_data_length_p = assoc_data_length;
++}
++
++void
++gssEapReleaseIov(gss_iov_buffer_desc *iov, int iov_count)
++{
++    int i;
++    OM_uint32 min_stat;
++
++    GSSEAP_ASSERT(iov != GSS_C_NO_IOV_BUFFER);
++
++    for (i = 0; i < iov_count; i++) {
++        if (iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
++            gss_release_buffer(&min_stat, &iov[i].buffer);
++            iov[i].type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
++        }
++    }
++}
++
++int
++gssEapIsIntegrityOnly(gss_iov_buffer_desc *iov, int iov_count)
++{
++    int i;
++    krb5_boolean has_conf_data = FALSE;
++
++    GSSEAP_ASSERT(iov != GSS_C_NO_IOV_BUFFER);
++
++    for (i = 0; i < iov_count; i++) {
++        if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA) {
++            has_conf_data = TRUE;
++            break;
++        }
++    }
++
++    return (has_conf_data == FALSE);
++}
++
++int
++gssEapAllocIov(gss_iov_buffer_t iov, size_t size)
++{
++    GSSEAP_ASSERT(iov != GSS_C_NO_IOV_BUFFER);
++    GSSEAP_ASSERT(iov->type & GSS_IOV_BUFFER_FLAG_ALLOCATE);
++
++    iov->buffer.length = size;
++    iov->buffer.value = GSSEAP_MALLOC(size);
++    if (iov->buffer.value == NULL) {
++        iov->buffer.length = 0;
++        return ENOMEM;
++    }
++
++    iov->type |= GSS_IOV_BUFFER_FLAG_ALLOCATED;
++
++    return 0;
++}
+diff --git a/mech_eap/util_json.cpp b/mech_eap/util_json.cpp
+new file mode 100644
+index 0000000..97eb1ed
+--- /dev/null
++++ b/mech_eap/util_json.cpp
+@@ -0,0 +1,513 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * JSONObject utilities.
++ */
++
++#include "gssapiP_eap.h"
++
++#include <typeinfo>
++#include <string>
++#include <sstream>
++#include <exception>
++#include <new>
++
++#define JSON_INIT(obj) do {                                     \
++        if ((obj) == NULL)                                      \
++            throw std::bad_alloc();                             \
++        m_obj = (obj);                                          \
++    } while (0)
++
++#define JSON_CHECK_CONTAINER() do {                             \
++        if (!json_is_object(m_obj) && !json_is_array(m_obj)) {  \
++            std::string s("JSONObject is not a container");     \
++            throw JSONException(m_obj);                         \
++        }                                                       \
++    } while (0)
++
++#define JSON_CHECK_OBJECT() do {                                \
++        if (!json_is_object(m_obj)) {                           \
++            std::string s("JSONObject is not a dictionary");    \
++            throw JSONException(m_obj, JSON_OBJECT);            \
++        }                                                       \
++    } while (0)
++
++#define JSON_CHECK_ARRAY() do {                                 \
++        if (!json_is_array(m_obj)) {                            \
++            throw JSONException(m_obj, JSON_ARRAY);             \
++        }                                                       \
++    } while (0)
++
++#define JSON_CHECK(s) do {                                      \
++        if ((s) != 0)                                           \
++            throw JSONException();                              \
++    } while (0)
++
++JSONObject
++JSONObject::load(const char *input, size_t flags, json_error_t *error)
++{
++    json_t *obj;
++
++    obj = json_loads(input, flags, error);
++
++    return JSONObject(obj, false);
++}
++
++JSONObject
++JSONObject::load(FILE *fp, size_t flags, json_error_t *error)
++{
++    json_t *obj;
++
++    obj = json_loadf(fp, flags, error);
++
++    return JSONObject(obj, false);
++}
++
++char *
++JSONObject::dump(size_t flags) const
++{
++    char *s = json_dumps(m_obj, flags);
++
++    if (s == NULL)
++        throw std::bad_alloc();
++
++    return s;
++}
++
++void
++JSONObject::dump(FILE *fp, size_t flags) const
++{
++    int r = json_dumpf(m_obj, fp, flags);
++
++    if (r != 0)
++        throw std::bad_alloc();
++}
++
++size_t
++JSONObject::size(void) const
++{
++    if (json_is_object(m_obj))
++        return json_object_size(m_obj);
++    else if (json_is_array(m_obj))
++        return json_array_size(m_obj);
++    else
++        return 0;
++}
++
++JSONObject::JSONObject(json_t *obj, bool retain)
++{
++    if (retain)
++        json_incref(obj);
++    JSON_INIT(obj);
++}
++
++JSONObject::JSONObject(const char *value)
++{
++    json_t *obj = json_string(value);
++
++    JSON_INIT(obj);
++}
++
++JSONObject::JSONObject(json_int_t value)
++{
++    json_t *obj = json_integer(value);
++
++    JSON_INIT(obj);
++}
++
++JSONObject::JSONObject(double value)
++{
++    json_t *obj = json_real(value);
++
++    JSON_INIT(obj);
++}
++
++JSONObject::JSONObject(bool value)
++{
++    json_t *obj = value ? json_true() : json_false();
++
++    JSON_INIT(obj);
++}
++
++JSONObject::JSONObject(void)
++{
++    json_t *obj = json_object();
++
++    JSON_INIT(obj);
++}
++
++JSONObject
++JSONObject::object(void)
++{
++    return JSONObject();
++}
++
++JSONObject
++JSONObject::null(void)
++{
++    return JSONObject(json_null(), false);
++}
++
++JSONObject
++JSONObject::array(void)
++{
++    return JSONObject(json_array(), false);
++}
++
++void
++JSONObject::set(const char *key, JSONObject &value)
++{
++    JSON_CHECK_OBJECT();
++    JSON_CHECK(json_object_set_new(m_obj, key, value.get()));
++}
++
++void
++JSONObject::set(const char *key, const char *value)
++{
++    JSONObject jobj(value);
++    set(key, jobj);
++}
++
++void
++JSONObject::set(const char *key, json_int_t value)
++{
++    JSONObject jobj(value);
++    set(key, jobj);
++}
++
++void
++JSONObject::del(const char *key)
++{
++    json_object_del(m_obj, key);
++}
++
++JSONObject
++JSONObject::get(const char *key) const
++{
++    json_t *obj;
++
++    obj = json_object_get(m_obj, key);
++    if (obj == NULL)
++        return JSONObject::null();
++
++    return JSONObject(obj, true);
++}
++
++JSONObject
++JSONObject::get(size_t index) const
++{
++    json_t *obj;
++
++    obj = json_array_get(m_obj, index);
++    if (obj == NULL)
++        return JSONObject::null();
++
++    return JSONObject(obj, true);
++}
++
++void
++JSONObject::update(JSONObject &value)
++{
++    JSON_CHECK_OBJECT();
++    json_t *other = value.get();
++    JSON_CHECK(json_object_update(m_obj, other));
++    json_decref(other);
++}
++
++JSONObject
++JSONObject::operator[](size_t index) const
++{
++    return get(index);
++}
++
++JSONObject
++JSONObject::operator[](const char *key) const
++{
++    return get(key);
++}
++
++void
++JSONObject::append(JSONObject &value)
++{
++    JSON_CHECK_ARRAY();
++    JSON_CHECK(json_array_append_new(m_obj, value.get()));
++}
++
++void
++JSONObject::insert(size_t index, JSONObject &value)
++{
++    JSON_CHECK_ARRAY();
++    JSON_CHECK(json_array_insert_new(m_obj, index, value.get()));
++}
++
++void
++JSONObject::remove(size_t index)
++{
++    JSON_CHECK_ARRAY();
++    JSON_CHECK(json_array_remove(m_obj, index));
++}
++
++void
++JSONObject::clear(void)
++{
++    JSON_CHECK_CONTAINER();
++
++    if (json_is_object(m_obj)) {
++        JSON_CHECK(json_object_clear(m_obj));
++    } else if (json_is_array(m_obj)) {
++        JSON_CHECK(json_array_clear(m_obj));
++    }
++}
++
++void
++JSONObject::extend(JSONObject &value)
++{
++    JSON_CHECK_ARRAY();
++    json_t *other = value.get();
++    JSON_CHECK(json_array_extend(m_obj, other));
++    json_decref(other);
++}
++
++const char *
++JSONObject::string(void) const
++{
++    return json_string_value(m_obj);
++}
++
++json_int_t
++JSONObject::integer(void) const
++{
++    return json_integer_value(m_obj);
++}
++
++double
++JSONObject::real(void) const
++{
++    return json_real_value(m_obj);
++}
++
++double
++JSONObject::number(void) const
++{
++    return json_number_value(m_obj);
++}
++
++#ifdef HAVE_SHIBRESOLVER
++JSONObject
++JSONObject::ddf(DDF &ddf)
++{
++    if (ddf.isstruct()) {
++        DDF elem = ddf.first();
++        JSONObject jobj = JSONObject::object();
++
++        while (!elem.isnull()) {
++            JSONObject jtmp = JSONObject::ddf(elem);
++            jobj.set(elem.name(), jtmp);
++            elem = ddf.next();
++        }
++
++        return jobj;
++    } else if (ddf.islist()) {
++        DDF elem = ddf.first();
++        JSONObject jobj = JSONObject::array();
++
++        while (!elem.isnull()) {
++            JSONObject jtmp = JSONObject::ddf(elem);
++            jobj.append(jtmp);
++            elem = ddf.next();
++        }
++
++        return jobj;
++    } else if (ddf.isstring()) {
++        return JSONObject(ddf.string());
++    } else if (ddf.isint()) {
++        return JSONObject((json_int_t)ddf.integer());
++    } else if (ddf.isfloat()) {
++        return JSONObject(ddf.floating());
++    } else if (ddf.isempty() || ddf.ispointer()) {
++        return JSONObject::object();
++    } else if (ddf.isnull()) {
++        return JSONObject::null();
++    }
++
++    std::string s("Unbridgeable DDF object");
++    throw JSONException();
++}
++
++DDF
++JSONObject::ddf(void) const
++{
++    DDF ddf(NULL);
++
++    switch (type()) {
++    case JSON_OBJECT: {
++        JSONIterator iter = iterator();
++
++        do {
++            const char *key = iter.key();
++            DDF value = iter.value().ddf();
++            ddf.addmember(key).swap(value);
++        } while (iter.next());
++        break;
++    }
++    case JSON_ARRAY: {
++        size_t i, nelems = size();
++
++        for (i = 0; i < nelems; i++) {
++            DDF value = get(i).ddf();
++            ddf.add(value);
++        }
++        break;
++    }
++    case JSON_STRING:
++        ddf.string(string());
++        break;
++    case JSON_INTEGER:
++        ddf.integer(integer());
++        break;
++    case JSON_REAL:
++        ddf.floating(real());
++        break;
++    case JSON_TRUE:
++        ddf.integer(1L);
++        break;
++    case JSON_FALSE:
++        ddf.integer(0L);
++        break;
++    case JSON_NULL:
++        break;
++    }
++
++    return ddf;
++}
++#endif /* HAVE_SHIBRESOLVER */
++
++bool JSONObject::isObject(void) const
++{
++    return json_is_object(m_obj);
++}
++
++bool JSONObject::isArray(void) const
++{
++    return json_is_array(m_obj);
++}
++
++bool JSONObject::isString(void) const
++{
++    return json_is_string(m_obj);
++}
++
++bool JSONObject::isInteger(void) const
++{
++    return json_is_integer(m_obj);
++}
++
++bool JSONObject::isNumber(void) const
++{
++    return json_is_number(m_obj);
++}
++
++bool JSONObject::isBoolean(void) const
++{
++    return json_is_boolean(m_obj);
++}
++
++bool JSONObject::isNull(void) const
++{
++    return json_is_null(m_obj);
++}
++
++JSONIterator::JSONIterator(const JSONObject &obj)
++{
++    m_obj = obj.get();
++    m_iter = json_object_iter(m_obj);
++}
++
++JSONIterator::~JSONIterator(void)
++{
++    json_decref(m_obj);
++}
++
++const char *
++JSONIterator::key(void) const
++{
++    return json_object_iter_key(m_iter);
++}
++
++JSONObject
++JSONIterator::value(void) const
++{
++    return JSONObject(json_object_iter_value(m_iter));
++}
++
++bool
++JSONIterator::next(void)
++{
++    m_iter = json_object_iter_next(m_obj, m_iter);
++    return m_iter != NULL;
++}
++
++JSONException::JSONException(json_t *obj, json_type type)
++{
++    char *s = NULL;
++    const char *t;
++
++    m_obj = json_incref(obj);
++    m_type = type;
++
++    if (obj != NULL)
++        s = json_dumps(m_obj, 0);
++
++    switch (type) {
++    case JSON_OBJECT:   t = "OBJECT";   break;
++    case JSON_ARRAY:    t = "ARRAY";    break;
++    case JSON_STRING:   t = "STRING";   break;
++    case JSON_INTEGER:  t = "INTEGER";  break;
++    case JSON_REAL:     t = "REAL";     break;
++    case JSON_TRUE:     t = "TRUE";     break;
++    case JSON_FALSE:    t = "FALSE";    break;
++    case JSON_NULL:     t = "NULL";     break;
++    default:            t = "UNKNOWN";  break;
++    }
++
++    if (obj != NULL) {
++        m_reason = "Invalid JSON object: " + std::string(s);
++        if (type != JSON_NULL)
++            m_reason += " (excepted type " + std::string(t) + ")";
++    } else {
++        m_reason = "Internal JSON error";
++    }
++
++    if (s != NULL)
++        GSSEAP_FREE(s);
++}
+diff --git a/mech_eap/util_json.h b/mech_eap/util_json.h
+new file mode 100644
+index 0000000..4ffecc8
+--- /dev/null
++++ b/mech_eap/util_json.h
+@@ -0,0 +1,182 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * JSON object wrapper with not-entirely-toll-free DDF bridging.
++ */
++
++#ifndef _UTIL_JSON_H_
++#define _UTIL_JSON_H_ 1
++
++#ifdef __cplusplus
++#include <string>
++#include <new>
++
++#include <jansson.h>
++
++#ifdef HAVE_SHIBRESOLVER
++#include <shibsp/remoting/ddf.h>
++using namespace shibsp;
++#endif
++
++namespace gss_eap_util {
++    class JSONObject;
++
++    class JSONException : public std::exception {
++    public:
++        JSONException(json_t *obj = NULL, json_type type = JSON_NULL);
++
++        ~JSONException(void) throw() {
++            json_decref(m_obj);
++        }
++
++        virtual const char *what(void) const throw() {
++            return m_reason.c_str();
++        }
++
++    private:
++        json_t *m_obj;
++        json_type m_type;
++        std::string m_reason;
++    };
++
++    class JSONIterator {
++    public:
++        JSONIterator(const JSONObject &obj);
++        ~JSONIterator(void);
++        const char *key(void) const;
++        JSONObject value(void) const;
++        bool next(void);
++
++    private:
++        json_t *m_obj;
++        void *m_iter;
++    };
++
++    class JSONObject {
++    public:
++        static JSONObject load(const char *input, size_t flags, json_error_t *error);
++        static JSONObject load(FILE *, size_t flags, json_error_t *error);
++
++        static JSONObject object(void);
++        static JSONObject array(void);
++        static JSONObject null(void);
++#ifdef HAVE_SHIBRESOLVER
++        static JSONObject ddf(DDF &value);
++#endif
++
++        char *dump(size_t flags = 0) const;
++        void dump(FILE *fp, size_t flags = JSON_INDENT(4)) const;
++
++        json_type type(void) const { return json_typeof(m_obj); }
++        size_t size(void) const;
++
++        JSONObject(void);
++        JSONObject(const char *value);
++        JSONObject(json_int_t value);
++        JSONObject(double value);
++        JSONObject(bool value);
++
++        void set(const char *key, JSONObject &value);
++        void set(const char *key, const char *value);
++        void set(const char *key, json_int_t value);
++        void del(const char *key);
++        void update(JSONObject &value);
++        JSONIterator iterator(void) const { return JSONIterator(*this); }
++        JSONObject get(const char *key) const;
++        JSONObject operator[](const char *key) const;
++
++        JSONObject get(size_t index) const;
++        JSONObject operator[](size_t index) const;
++        void append(JSONObject &value);
++        void insert(size_t index, JSONObject &value);
++        void remove(size_t index);
++        void clear(void);
++        void extend(JSONObject &value);
++
++        const char *string(void) const;
++        json_int_t integer(void) const;
++        double real(void) const;
++        double number(void) const;
++#ifdef HAVE_SHIBRESOLVER
++        DDF ddf(void) const;
++#endif
++
++        bool isObject(void) const;
++        bool isArray(void) const;
++        bool isString(void) const;
++        bool isInteger(void) const;
++        bool isNumber(void) const;
++        bool isBoolean(void) const;
++        bool isNull(void) const;
++
++        ~JSONObject(void)
++        {
++            if (m_obj != NULL)
++                json_decref(m_obj);
++        }
++
++        JSONObject(const JSONObject &obj)
++        {
++            m_obj = json_incref(obj.m_obj);
++        }
++
++        JSONObject& operator=(const JSONObject &obj)
++        {
++            if (this != &obj)
++                set(obj.m_obj);
++            return *this;
++        }
++
++    private:
++        friend class JSONIterator;
++
++        json_t *get(void) const {
++            return json_incref(m_obj);
++        }
++
++        void set(json_t *obj) {
++            if (m_obj != obj) {
++                json_decref(m_obj);
++                m_obj = json_incref(m_obj);
++            }
++        }
++
++        JSONObject(json_t *obj, bool retain = true);
++
++        json_t *m_obj;
++    };
++}
++
++#endif /* __cplusplus */
++
++#endif /* _UTIL_JSON_H_ */
+diff --git a/mech_eap/util_krb.c b/mech_eap/util_krb.c
+new file mode 100644
+index 0000000..5eaa31e
+--- /dev/null
++++ b/mech_eap/util_krb.c
+@@ -0,0 +1,632 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Kerberos 5 helpers.
++ */
++
++#include "gssapiP_eap.h"
++
++void
++gssEapDestroyKrbContext(krb5_context context)
++{
++    if (context != NULL)
++        krb5_free_context(context);
++}
++
++static krb5_error_code
++initKrbContext(krb5_context *pKrbContext)
++{
++    krb5_context krbContext;
++    krb5_error_code code;
++    char *defaultRealm = NULL;
++
++    *pKrbContext = NULL;
++
++    code = krb5_init_context(&krbContext);
++    if (code != 0)
++        goto cleanup;
++
++    krb5_appdefault_string(krbContext, "eap_gss",
++                           NULL, "default_realm", "", &defaultRealm);
++
++    if (defaultRealm != NULL && defaultRealm[0] != '\0') {
++        code = krb5_set_default_realm(krbContext, defaultRealm);
++        if (code != 0)
++            goto cleanup;
++    }
++
++    *pKrbContext = krbContext;
++
++cleanup:
++    krb5_free_default_realm(krbContext, defaultRealm);
++
++    if (code != 0 && krbContext != NULL)
++        krb5_free_context(krbContext);
++
++    return code;
++}
++
++OM_uint32
++gssEapKerberosInit(OM_uint32 *minor, krb5_context *context)
++{
++    struct gss_eap_thread_local_data *tld;
++
++    *minor = 0;
++    *context = NULL;
++
++    tld = gssEapGetThreadLocalData();
++    if (tld != NULL) {
++        if (tld->krbContext == NULL) {
++            *minor = initKrbContext(&tld->krbContext);
++            if (*minor != 0)
++                tld->krbContext = NULL;
++        }
++        *context = tld->krbContext;
++    } else {
++        *minor = GSSEAP_GET_LAST_ERROR();
++    }
++
++    GSSEAP_ASSERT(*context != NULL || *minor != 0);
++
++    return (*minor == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
++}
++
++/*
++ * Derive a key K for RFC 4121 use by using the following
++ * derivation function (based on RFC 4402);
++ *
++ * KMSK = random-to-key(MSK)
++ * Tn = pseudo-random(KMSK, n || "rfc4121-gss-eap")
++ * L = output key size
++ * K = truncate(L, T1 || T2 || .. || Tn)
++ *
++ * The output must be freed by krb5_free_keyblock_contents(),
++ * not GSSEAP_FREE().
++ */
++OM_uint32
++gssEapDeriveRfc3961Key(OM_uint32 *minor,
++                       const unsigned char *inputKey,
++                       size_t inputKeyLength,
++                       krb5_enctype encryptionType,
++                       krb5_keyblock *pKey)
++{
++    krb5_context krbContext;
++#ifndef HAVE_HEIMDAL_VERSION
++    krb5_data data;
++#endif
++    krb5_data ns, t, derivedKeyData;
++    krb5_keyblock kd;
++    krb5_error_code code;
++    size_t randomLength, keyLength, prfLength;
++    unsigned char constant[4 + sizeof("rfc4121-gss-eap") - 1], *p;
++    ssize_t i, remain;
++
++    GSSEAP_KRB_INIT(&krbContext);
++    GSSEAP_ASSERT(encryptionType != ENCTYPE_NULL);
++
++    KRB_KEY_INIT(pKey);
++    KRB_KEY_INIT(&kd);
++    KRB_KEY_TYPE(&kd) = encryptionType;
++
++    KRB_DATA_INIT(&ns);
++    KRB_DATA_INIT(&t);
++    KRB_DATA_INIT(&derivedKeyData);
++
++    code = krb5_c_keylengths(krbContext, encryptionType,
++                             &randomLength, &keyLength);
++    if (code != 0)
++        goto cleanup;
++
++    /* Convert EAP MSK into a Kerberos key */
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_random_to_key(krbContext, encryptionType, inputKey,
++                              MIN(inputKeyLength, randomLength), &kd);
++#else
++    data.length = MIN(inputKeyLength, randomLength);
++    data.data = (char *)inputKey;
++
++    KRB_KEY_DATA(&kd) = KRB_MALLOC(keyLength);
++    if (KRB_KEY_DATA(&kd) == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++    KRB_KEY_LENGTH(&kd) = keyLength;
++
++    code = krb5_c_random_to_key(krbContext, encryptionType, &data, &kd);
++#endif /* HAVE_HEIMDAL_VERSION */
++    if (code != 0)
++        goto cleanup;
++
++    memset(&constant[0], 0, 4);
++    memcpy(&constant[4], "rfc4121-gss-eap", sizeof("rfc4121-gss-eap") - 1);
++
++    ns.length = sizeof(constant);
++    ns.data = (char *)constant;
++
++    /* Plug derivation constant and key into PRF */
++    code = krb5_c_prf_length(krbContext, encryptionType, &prfLength);
++    if (code != 0)
++        goto cleanup;
++
++#ifndef HAVE_HEIMDAL_VERSION
++    /* Same API, but different allocation rules, unfortunately. */
++    t.length = prfLength;
++    t.data = GSSEAP_MALLOC(t.length);
++    if (t.data == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++#endif
++
++    derivedKeyData.length = randomLength;
++    derivedKeyData.data = GSSEAP_MALLOC(derivedKeyData.length);
++    if (derivedKeyData.data == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++
++    for (i = 0, p = (unsigned char *)derivedKeyData.data, remain = randomLength;
++         remain > 0;
++         p += t.length, remain -= t.length, i++)
++    {
++        store_uint32_be(i, ns.data);
++
++        code = krb5_c_prf(krbContext, &kd, &ns, &t);
++        if (code != 0)
++            goto cleanup;
++
++        memcpy(p, t.data, MIN(t.length, remain));
++     }
++
++    /* Finally, convert PRF output into a new key which we will return */
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_free_keyblock_contents(krbContext, &kd);
++    KRB_KEY_INIT(&kd);
++
++    code = krb5_random_to_key(krbContext, encryptionType,
++                              derivedKeyData.data, derivedKeyData.length, &kd);
++#else
++    code = krb5_c_random_to_key(krbContext, encryptionType,
++                                &derivedKeyData, &kd);
++#endif
++    if (code != 0)
++        goto cleanup;
++
++    *pKey = kd;
++
++cleanup:
++    if (code != 0)
++        krb5_free_keyblock_contents(krbContext, &kd);
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_free_data_contents(krbContext, &t);
++#else
++    if (t.data != NULL) {
++        memset(t.data, 0, t.length);
++        GSSEAP_FREE(t.data);
++    }
++#endif
++    if (derivedKeyData.data != NULL) {
++        memset(derivedKeyData.data, 0, derivedKeyData.length);
++        GSSEAP_FREE(derivedKeyData.data);
++    }
++
++    *minor = code;
++
++    return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
++}
++
++#ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE
++extern krb5_error_code
++krb5int_c_mandatory_cksumtype(krb5_context, krb5_enctype, krb5_cksumtype *);
++#endif
++
++OM_uint32
++rfc3961ChecksumTypeForKey(OM_uint32 *minor,
++                          krb5_keyblock *key,
++                          krb5_cksumtype *cksumtype)
++{
++    krb5_context krbContext;
++#ifndef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE
++    krb5_data data;
++    krb5_checksum cksum;
++#endif
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++#ifdef HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE
++    *minor = krb5int_c_mandatory_cksumtype(krbContext, KRB_KEY_TYPE(key),
++                                           cksumtype);
++    if (*minor != 0)
++        return GSS_S_FAILURE;
++#else
++    KRB_DATA_INIT(&data);
++
++    memset(&cksum, 0, sizeof(cksum));
++
++    /*
++     * This is a complete hack but it's the only way to work with
++     * MIT Kerberos pre-1.9 without using private API, as it does
++     * not support passing in zero as the checksum type.
++     */
++    *minor = krb5_c_make_checksum(krbContext, 0, key, 0, &data, &cksum);
++    if (*minor != 0)
++        return GSS_S_FAILURE;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    *cksumtype = cksum.cksumtype;
++#else
++    *cksumtype = cksum.checksum_type;
++#endif
++
++    krb5_free_checksum_contents(krbContext, &cksum);
++#endif /* HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE */
++
++    if (!krb5_c_is_keyed_cksum(*cksumtype)) {
++        *minor = (OM_uint32)KRB5KRB_AP_ERR_INAPP_CKSUM;
++        return GSS_S_FAILURE;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++krb5_error_code
++krbCryptoLength(krb5_context krbContext,
++#ifdef HAVE_HEIMDAL_VERSION
++                krb5_crypto krbCrypto,
++#else
++                krb5_keyblock *key,
++#endif
++                int type,
++                size_t *length)
++{
++#ifdef HAVE_HEIMDAL_VERSION
++    return krb5_crypto_length(krbContext, krbCrypto, type, length);
++#else
++    unsigned int len;
++    krb5_error_code code;
++
++    code = krb5_c_crypto_length(krbContext, KRB_KEY_TYPE(key), type, &len);
++    if (code == 0)
++        *length = (size_t)len;
++
++    return code;
++#endif
++}
++
++krb5_error_code
++krbPaddingLength(krb5_context krbContext,
++#ifdef HAVE_HEIMDAL_VERSION
++                 krb5_crypto krbCrypto,
++#else
++                 krb5_keyblock *key,
++#endif
++                 size_t dataLength,
++                 size_t *padLength)
++{
++    krb5_error_code code;
++#ifdef HAVE_HEIMDAL_VERSION
++    size_t headerLength, paddingLength;
++
++    code = krbCryptoLength(krbContext, krbCrypto,
++                           KRB5_CRYPTO_TYPE_HEADER, &headerLength);
++    if (code != 0)
++        return code;
++
++    dataLength += headerLength;
++
++    code = krb5_crypto_length(krbContext, krbCrypto,
++                              KRB5_CRYPTO_TYPE_PADDING, &paddingLength);
++    if (code != 0)
++        return code;
++
++    if (paddingLength != 0 && (dataLength % paddingLength) != 0)
++        *padLength = paddingLength - (dataLength % paddingLength);
++    else
++        *padLength = 0;
++
++    return 0;
++#else
++    unsigned int pad;
++
++    code = krb5_c_padding_length(krbContext, KRB_KEY_TYPE(key), dataLength, &pad);
++    if (code == 0)
++        *padLength = (size_t)pad;
++
++    return code;
++#endif /* HAVE_HEIMDAL_VERSION */
++}
++
++krb5_error_code
++krbBlockSize(krb5_context krbContext,
++#ifdef HAVE_HEIMDAL_VERSION
++                 krb5_crypto krbCrypto,
++#else
++                 krb5_keyblock *key,
++#endif
++                 size_t *blockSize)
++{
++#ifdef HAVE_HEIMDAL_VERSION
++    return krb5_crypto_getblocksize(krbContext, krbCrypto, blockSize);
++#else
++    return krb5_c_block_size(krbContext, KRB_KEY_TYPE(key), blockSize);
++#endif
++}
++
++krb5_error_code
++krbEnctypeToString(
++#ifdef HAVE_HEIMDAL_VERSION
++                   krb5_context krbContext,
++#else
++                   krb5_context krbContext GSSEAP_UNUSED,
++#endif
++                   krb5_enctype enctype,
++                   const char *prefix,
++                   gss_buffer_t string)
++{
++    krb5_error_code code;
++#ifdef HAVE_HEIMDAL_VERSION
++    char *enctypeBuf = NULL;
++#else
++    char enctypeBuf[128];
++#endif
++    size_t prefixLength, enctypeLength;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_enctype_to_string(krbContext, enctype, &enctypeBuf);
++#else
++    code = krb5_enctype_to_name(enctype, 0, enctypeBuf, sizeof(enctypeBuf));
++#endif
++    if (code != 0)
++        return code;
++
++    prefixLength = (prefix != NULL) ? strlen(prefix) : 0;
++    enctypeLength = strlen(enctypeBuf);
++
++    string->value = GSSEAP_MALLOC(prefixLength + enctypeLength + 1);
++    if (string->value == NULL) {
++#ifdef HAVE_HEIMDAL_VERSION
++        krb5_xfree(enctypeBuf);
++#endif
++        return ENOMEM;
++    }
++
++    if (prefixLength != 0)
++        memcpy(string->value, prefix, prefixLength);
++    memcpy((char *)string->value + prefixLength, enctypeBuf, enctypeLength);
++
++    string->length = prefixLength + enctypeLength;
++    ((char *)string->value)[string->length] = '\0';
++
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_xfree(enctypeBuf);
++#endif
++
++    return 0;
++}
++
++krb5_error_code
++krbMakeAuthDataKdcIssued(krb5_context context,
++                         const krb5_keyblock *key,
++                         krb5_const_principal issuer,
++#ifdef HAVE_HEIMDAL_VERSION
++                         const AuthorizationData *authdata,
++                         AuthorizationData *adKdcIssued
++#else
++                         krb5_authdata *const *authdata,
++                         krb5_authdata ***adKdcIssued
++#endif
++                         )
++{
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_error_code code;
++    AD_KDCIssued kdcIssued;
++    AuthorizationDataElement adDatum;
++    unsigned char *buf;
++    size_t buf_size, len;
++    krb5_crypto crypto = NULL;
++
++    memset(&kdcIssued, 0, sizeof(kdcIssued));
++    memset(adKdcIssued, 0, sizeof(*adKdcIssued));
++
++    kdcIssued.i_realm = issuer->realm != NULL ? (Realm *)&issuer->realm : NULL;
++    kdcIssued.i_sname = (PrincipalName *)&issuer->name;
++    kdcIssued.elements = *authdata;
++
++    ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, &len, code);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_crypto_init(context, key, 0, &crypto);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_create_checksum(context, crypto, KRB5_KU_AD_KDC_ISSUED,
++                                0, buf, buf_size, &kdcIssued.ad_checksum);
++    if (code != 0)
++        goto cleanup;
++
++    free(buf); /* match ASN1_MALLOC_ENCODE */
++    buf = NULL;
++
++    ASN1_MALLOC_ENCODE(AD_KDCIssued, buf, buf_size, &kdcIssued, &len, code);
++    if (code != 0)
++        goto cleanup;
++
++    adDatum.ad_type = KRB5_AUTHDATA_KDC_ISSUED;
++    adDatum.ad_data.length = buf_size;
++    adDatum.ad_data.data = buf;
++
++    code = add_AuthorizationData(adKdcIssued, &adDatum);
++    if (code != 0)
++        goto cleanup;
++
++cleanup:
++    if (buf != NULL)
++        free(buf); /* match ASN1_MALLOC_ENCODE */
++    if (crypto != NULL)
++        krb5_crypto_destroy(context, crypto);
++    free_Checksum(&kdcIssued.ad_checksum);
++
++    return code;
++#else
++    return krb5_make_authdata_kdc_issued(context, key, issuer, authdata,
++                                         adKdcIssued);
++#endif /* HAVE_HEIMDAL_VERSION */
++}
++
++krb5_error_code
++krbMakeCred(krb5_context krbContext,
++            krb5_auth_context authContext,
++            krb5_creds *creds,
++            krb5_data *data)
++{
++    krb5_error_code code;
++#ifdef HAVE_HEIMDAL_VERSION
++    KRB_CRED krbCred;
++    KrbCredInfo krbCredInfo;
++    EncKrbCredPart encKrbCredPart;
++    krb5_keyblock *key;
++    krb5_crypto krbCrypto = NULL;
++    krb5_data encKrbCredPartData;
++    krb5_replay_data rdata;
++    size_t len;
++#else
++    krb5_data *d = NULL;
++#endif
++
++    memset(data, 0, sizeof(*data));
++#ifdef HAVE_HEIMDAL_VERSION
++    memset(&krbCred,        0, sizeof(krbCred));
++    memset(&krbCredInfo,    0, sizeof(krbCredInfo));
++    memset(&encKrbCredPart, 0, sizeof(encKrbCredPart));
++    memset(&rdata,          0, sizeof(rdata));
++
++    if (authContext->local_subkey)
++        key = authContext->local_subkey;
++    else if (authContext->remote_subkey)
++        key = authContext->remote_subkey;
++    else
++        key = authContext->keyblock;
++
++    krbCred.pvno = 5;
++    krbCred.msg_type = krb_cred;
++    krbCred.tickets.val = (Ticket *)GSSEAP_CALLOC(1, sizeof(Ticket));
++    if (krbCred.tickets.val == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++    krbCred.tickets.len = 1;
++
++    code = decode_Ticket(creds->ticket.data,
++                         creds->ticket.length,
++                         krbCred.tickets.val, &len);
++    if (code != 0)
++        goto cleanup;
++
++    krbCredInfo.key         = creds->session;
++    krbCredInfo.prealm      = &creds->client->realm;
++    krbCredInfo.pname       = &creds->client->name;
++    krbCredInfo.flags       = &creds->flags.b;
++    krbCredInfo.authtime    = &creds->times.authtime;
++    krbCredInfo.starttime   = &creds->times.starttime;
++    krbCredInfo.endtime     = &creds->times.endtime;
++    krbCredInfo.renew_till  = &creds->times.renew_till;
++    krbCredInfo.srealm      = &creds->server->realm;
++    krbCredInfo.sname       = &creds->server->name;
++    krbCredInfo.caddr       = creds->addresses.len ? &creds->addresses : NULL;
++
++    encKrbCredPart.ticket_info.len = 1;
++    encKrbCredPart.ticket_info.val = &krbCredInfo;
++    if (authContext->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
++        rdata.seq                  = authContext->local_seqnumber;
++        encKrbCredPart.nonce       = (int32_t *)&rdata.seq;
++    } else {
++        encKrbCredPart.nonce       = NULL;
++    }
++    if (authContext->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
++        krb5_us_timeofday(krbContext, &rdata.timestamp, &rdata.usec);
++        encKrbCredPart.timestamp   = &rdata.timestamp;
++        encKrbCredPart.usec        = &rdata.usec;
++    } else {
++        encKrbCredPart.timestamp   = NULL;
++        encKrbCredPart.usec        = NULL;
++    }
++    encKrbCredPart.s_address       = authContext->local_address;
++    encKrbCredPart.r_address       = authContext->remote_address;
++
++    ASN1_MALLOC_ENCODE(EncKrbCredPart, encKrbCredPartData.data,
++                       encKrbCredPartData.length, &encKrbCredPart,
++                       &len, code);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_crypto_init(krbContext, key, 0, &krbCrypto);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_encrypt_EncryptedData(krbContext,
++                                      krbCrypto,
++                                      KRB5_KU_KRB_CRED,
++                                      encKrbCredPartData.data,
++                                      encKrbCredPartData.length,
++                                      0,
++                                      &krbCred.enc_part);
++    if (code != 0)
++        goto cleanup;
++
++    ASN1_MALLOC_ENCODE(KRB_CRED, data->data, data->length,
++                       &krbCred, &len, code);
++    if (code != 0)
++        goto cleanup;
++
++    if (authContext->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
++        authContext->local_seqnumber++;
++
++cleanup:
++    if (krbCrypto != NULL)
++        krb5_crypto_destroy(krbContext, krbCrypto);
++    free_KRB_CRED(&krbCred);
++    krb5_data_free(&encKrbCredPartData);
++
++    return code;
++#else
++    code = krb5_mk_1cred(krbContext, authContext, creds, &d, NULL);
++    if (code == 0) {
++        *data = *d;
++        GSSEAP_FREE(d);
++    }
++
++    return code;
++#endif /* HAVE_HEIMDAL_VERSION */
++}
+diff --git a/mech_eap/util_lucid.c b/mech_eap/util_lucid.c
+new file mode 100644
+index 0000000..f9e9941
+--- /dev/null
++++ b/mech_eap/util_lucid.c
+@@ -0,0 +1,183 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * "Lucid" security context export routine (called by MIT Kerberos mechanism).
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++gssEapExportLucidSecContext(OM_uint32 *minor,
++                            gss_ctx_id_t ctx,
++                            const gss_OID desiredObject GSSEAP_UNUSED,
++                            gss_buffer_set_t *data_set)
++{
++    OM_uint32 major = GSS_S_COMPLETE;
++    int haveAcceptorSubkey =
++        ((rfc4121Flags(ctx, 0) & TOK_FLAG_ACCEPTOR_SUBKEY) != 0);
++    gss_buffer_desc rep;
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_error_code code;
++    krb5_storage *sp;
++    krb5_data data = { 0 };
++
++    sp = krb5_storage_emem();
++    if (sp == NULL) {
++        code = ENOMEM;
++        goto cleanup;
++    }
++
++    code = krb5_store_int32(sp, 1);     /* version */
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, CTX_IS_INITIATOR(ctx));
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, ctx->expiryTime); 
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, 0);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, ctx->sendSeq);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, 0);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, ctx->recvSeq);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, 1);     /* is_cfx */
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_int32(sp, haveAcceptorSubkey);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_store_keyblock(sp, ctx->rfc3961Key);
++    if (code != 0)
++        goto cleanup;
++
++    if (haveAcceptorSubkey) {
++        code = krb5_store_keyblock(sp, ctx->rfc3961Key);
++        if (code != 0)
++            goto cleanup;
++    }
++
++    code = krb5_storage_to_data(sp, &data);
++    if (code != 0)
++        goto cleanup;
++
++    rep.length = data.length;
++    rep.value = data.data;
++
++    major = gss_add_buffer_set_member(minor, &rep, data_set);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    krb5_data_free(&data);
++
++    if (major == GSS_S_COMPLETE) {
++        *minor = code;
++        major = (code != 0) ? GSS_S_FAILURE : GSS_S_COMPLETE;
++    }
++
++    return major;
++#else
++    gss_krb5_lucid_context_v1_t *lctx;
++    gss_krb5_lucid_key_t *lkey = NULL;
++
++    lctx = (gss_krb5_lucid_context_v1_t *)GSSEAP_CALLOC(1, sizeof(*lctx));
++    if (lctx == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++
++    lctx->version = 1;
++    lctx->initiate = CTX_IS_INITIATOR(ctx);
++    if (ctx->expiryTime == 0)
++        lctx->endtime = KRB_TIME_FOREVER;
++    else
++        lctx->endtime = ctx->expiryTime;
++    lctx->send_seq = ctx->sendSeq;
++    lctx->recv_seq = ctx->recvSeq;
++    lctx->protocol = 1;
++
++    lctx->cfx_kd.have_acceptor_subkey = haveAcceptorSubkey;
++
++    lkey = haveAcceptorSubkey
++           ? &lctx->cfx_kd.acceptor_subkey
++           : &lctx->cfx_kd.ctx_key;
++
++    lkey->type = KRB_KEY_TYPE(&ctx->rfc3961Key);
++    lkey->data = GSSEAP_MALLOC(KRB_KEY_LENGTH(&ctx->rfc3961Key));
++    if (lkey->data == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++    lkey->length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
++    memcpy(lkey->data, KRB_KEY_DATA(&ctx->rfc3961Key), lkey->length);
++
++    rep.value = &lctx;
++    rep.length = sizeof(void *);
++
++    major = gss_add_buffer_set_member(minor, &rep, data_set);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        if (lctx != NULL) {
++            if (lkey != NULL && lkey->data != NULL) {
++                memset(lkey->data, 0, lkey->length);
++                GSSEAP_FREE(lkey->data);
++            }
++            GSSEAP_FREE(lctx);
++        }
++    }
++
++    return major;
++#endif /* HAVE_HEIMDAL_VERSION */
++}
+diff --git a/mech_eap/util_mech.c b/mech_eap/util_mech.c
+new file mode 100644
+index 0000000..958e43d
+--- /dev/null
++++ b/mech_eap/util_mech.c
+@@ -0,0 +1,380 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * General mechanism utility routines.
++ */
++
++#include "gssapiP_eap.h"
++
++/*
++ * 1.3.6.1.4.1.5322(padl)
++ *      gssEap(22)
++ *       mechanisms(1)
++ *        eap-aes128-cts-hmac-sha1-96(17)
++ *        eap-aes256-cts-hmac-sha1-96(18)
++ *       nameTypes(2)
++ *       apiExtensions(3)
++ *        inquireSecContextByOid(1)
++ *        inquireCredByOid(2)
++ *        setSecContextOption(3)
++ *        setCredOption(4)
++ *        mechInvoke(5)
++ */
++
++/*
++ * Note: the enctype-less OID is used as the mechanism OID in non-
++ * canonicalized exported names.
++ */
++static gss_OID_desc gssEapMechOids[] = {
++    /* 1.3.6.1.4.1.5322.22.1  */
++    { 9, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01" },
++    /* 1.3.6.1.4.1.5322.22.1.17 */
++    { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" },
++    /* 1.3.6.1.4.1.5322.22.1.18 */
++    { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x12" }
++};
++
++gss_OID GSS_EAP_MECHANISM                            = &gssEapMechOids[0];
++gss_OID GSS_EAP_AES128_CTS_HMAC_SHA1_96_MECHANISM    = &gssEapMechOids[1];
++gss_OID GSS_EAP_AES256_CTS_HMAC_SHA1_96_MECHANISM    = &gssEapMechOids[2];
++
++static int
++internalizeOid(const gss_OID oid,
++               gss_OID *const pInternalizedOid);
++
++/*
++ * Returns TRUE is the OID is a concrete mechanism OID, that is, one
++ * with a Kerberos enctype as the last element.
++ */
++int
++gssEapIsConcreteMechanismOid(const gss_OID oid)
++{
++    return oid->length > GSS_EAP_MECHANISM->length &&
++           memcmp(oid->elements, GSS_EAP_MECHANISM->elements,
++                  GSS_EAP_MECHANISM->length) == 0;
++}
++
++int
++gssEapIsMechanismOid(const gss_OID oid)
++{
++    return oid == GSS_C_NO_OID ||
++           oidEqual(oid, GSS_EAP_MECHANISM) ||
++           gssEapIsConcreteMechanismOid(oid);
++}
++
++/*
++ * Validate that all elements are concrete mechanism OIDs.
++ */
++OM_uint32
++gssEapValidateMechs(OM_uint32 *minor,
++                    const gss_OID_set mechs)
++{
++    int i;
++
++    *minor = 0;
++
++    if (mechs == GSS_C_NO_OID_SET) {
++        return GSS_S_COMPLETE;
++    }
++
++    for (i = 0; i < mechs->count; i++) {
++        gss_OID oid = &mechs->elements[i];
++
++        if (!gssEapIsConcreteMechanismOid(oid)) {
++            *minor = GSSEAP_WRONG_MECH;
++            return GSS_S_BAD_MECH;
++        }
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapOidToEnctype(OM_uint32 *minor,
++                   const gss_OID oid,
++                   krb5_enctype *enctype)
++{
++    OM_uint32 major;
++    int suffix;
++
++    major = decomposeOid(minor,
++                         GSS_EAP_MECHANISM->elements,
++                         GSS_EAP_MECHANISM->length,
++                         oid,
++                         &suffix);
++    if (major == GSS_S_COMPLETE)
++        *enctype = suffix;
++
++    return major;
++}
++
++OM_uint32
++gssEapEnctypeToOid(OM_uint32 *minor,
++                   krb5_enctype enctype,
++                   gss_OID *pOid)
++{
++    OM_uint32 major;
++    gss_OID oid;
++
++    *pOid = NULL;
++
++    oid = (gss_OID)GSSEAP_MALLOC(sizeof(*oid));
++    if (oid == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    oid->length = GSS_EAP_MECHANISM->length + 1;
++    oid->elements = GSSEAP_MALLOC(oid->length);
++    if (oid->elements == NULL) {
++        *minor = ENOMEM;
++        GSSEAP_FREE(oid);
++        return GSS_S_FAILURE;
++    }
++
++    major = composeOid(minor,
++                       GSS_EAP_MECHANISM->elements,
++                       GSS_EAP_MECHANISM->length,
++                       enctype,
++                       oid);
++    if (major == GSS_S_COMPLETE) {
++        internalizeOid(oid, pOid);
++        *pOid = oid;
++    } else {
++        GSSEAP_FREE(oid->elements);
++        GSSEAP_FREE(oid);
++    }
++
++    return major;
++}
++
++OM_uint32
++gssEapIndicateMechs(OM_uint32 *minor,
++                    gss_OID_set *mechs)
++{
++    krb5_context krbContext;
++    OM_uint32 major;
++    krb5_enctype *etypes;
++    int i;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    *minor = krb5_get_permitted_enctypes(krbContext, &etypes);
++    if (*minor != 0) {
++        return GSS_S_FAILURE;
++    }
++
++    major = gss_create_empty_oid_set(minor, mechs);
++    if (GSS_ERROR(major)) {
++        GSSEAP_FREE(etypes);
++        return major;
++    }
++
++    for (i = 0; etypes[i] != ENCTYPE_NULL; i++) {
++        gss_OID mechOid;
++#ifndef HAVE_HEIMDAL_VERSION
++        OM_uint32 tmpMinor;
++#endif
++
++        /* XXX currently we aren't equipped to encode these enctypes */
++        if (etypes[i] < 0 || etypes[i] > 127)
++            continue;
++
++        major = gssEapEnctypeToOid(minor, etypes[i], &mechOid);
++        if (GSS_ERROR(major))
++            break;
++
++        major = gss_add_oid_set_member(minor, mechOid, mechs);
++        if (GSS_ERROR(major))
++            break;
++
++#ifndef HAVE_HEIMDAL_VERSION
++        gss_release_oid(&tmpMinor, &mechOid);
++#endif
++    }
++
++    GSSEAP_FREE(etypes);
++
++    *minor = 0;
++    return major;
++}
++
++OM_uint32
++gssEapDefaultMech(OM_uint32 *minor,
++                  gss_OID *oid)
++{
++    gss_OID_set mechs;
++    OM_uint32 major, tmpMinor;
++
++    major = gssEapIndicateMechs(minor, &mechs);
++    if (GSS_ERROR(major)) {
++        return major;
++    }
++
++    if (mechs->count == 0) {
++        gss_release_oid_set(&tmpMinor, &mechs);
++        return GSS_S_BAD_MECH;
++    }
++
++    if (!internalizeOid(&mechs->elements[0], oid)) {
++        /* don't double-free if we didn't internalize it */
++        mechs->elements[0].length = 0;
++        mechs->elements[0].elements = NULL;
++    }
++
++    gss_release_oid_set(&tmpMinor, &mechs);
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static int
++internalizeOid(const gss_OID oid,
++               gss_OID *const pInternalizedOid)
++{
++    int i;
++
++    *pInternalizedOid = GSS_C_NO_OID;
++
++    for (i = 0;
++         i < sizeof(gssEapMechOids) / sizeof(gssEapMechOids[0]);
++         i++) {
++        if (oidEqual(oid, &gssEapMechOids[i])) {
++            *pInternalizedOid = (const gss_OID)&gssEapMechOids[i];
++            break;
++        }
++    }
++
++    if (*pInternalizedOid == GSS_C_NO_OID) {
++        if (oidEqual(oid, GSS_EAP_NT_EAP_NAME))
++            *pInternalizedOid = (const gss_OID)GSS_EAP_NT_EAP_NAME;
++    }
++
++    if (*pInternalizedOid == GSS_C_NO_OID) {
++        *pInternalizedOid = oid;
++        return 0;
++    }
++
++    return 1;
++}
++
++OM_uint32
++gssEapReleaseOid(OM_uint32 *minor, gss_OID *oid)
++{
++    gss_OID internalizedOid = GSS_C_NO_OID;
++
++    *minor = 0;
++
++    if (internalizeOid(*oid, &internalizedOid)) {
++        /* OID was internalized, so we can mark it as "freed" */
++        *oid = GSS_C_NO_OID;
++        return GSS_S_COMPLETE;
++    }
++
++    /* we don't know about this OID */
++    return GSS_S_CONTINUE_NEEDED;
++}
++
++OM_uint32
++gssEapCanonicalizeOid(OM_uint32 *minor,
++                      const gss_OID oid,
++                      OM_uint32 flags,
++                      gss_OID *pOid)
++{
++    OM_uint32 major;
++    int mapToNull = 0;
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++    *pOid = GSS_C_NULL_OID;
++
++    if (oid == GSS_C_NULL_OID) {
++        if ((flags & OID_FLAG_NULL_VALID) == 0) {
++            *minor = GSSEAP_WRONG_MECH;
++            return GSS_S_BAD_MECH;
++        } else if (flags & OID_FLAG_MAP_NULL_TO_DEFAULT_MECH) {
++            return gssEapDefaultMech(minor, pOid);
++        } else {
++            mapToNull = 1;
++        }
++    } else if (oidEqual(oid, GSS_EAP_MECHANISM)) {
++        if ((flags & OID_FLAG_FAMILY_MECH_VALID) == 0) {
++            *minor = GSSEAP_WRONG_MECH;
++            return GSS_S_BAD_MECH;
++        } else if (flags & OID_FLAG_MAP_FAMILY_MECH_TO_NULL) {
++            mapToNull = 1;
++        }
++    } else if (!gssEapIsConcreteMechanismOid(oid)) {
++        *minor = GSSEAP_WRONG_MECH;
++        return GSS_S_BAD_MECH;
++    }
++
++    if (!mapToNull) {
++        if (!internalizeOid(oid, pOid))
++            major = duplicateOid(minor, oid, pOid);
++    }
++
++    return major;
++}
++
++static gss_buffer_desc gssEapSaslMechs[] = {
++    { sizeof("EAP") - 1,        "EAP",       }, /* not used */
++    { sizeof("EAP-AES128") - 1, "EAP-AES128" },
++    { sizeof("EAP-AES256") - 1, "EAP-AES256" },
++};
++
++gss_buffer_t
++gssEapOidToSaslName(const gss_OID oid)
++{
++    size_t i;
++
++    for (i = 1; i < sizeof(gssEapMechOids)/sizeof(gssEapMechOids[0]); i++) {
++        if (oidEqual(&gssEapMechOids[i], oid))
++            return &gssEapSaslMechs[i];
++    }
++
++    return GSS_C_NO_BUFFER;
++}
++
++gss_OID
++gssEapSaslNameToOid(const gss_buffer_t name)
++{
++    size_t i;
++
++    for (i = 1; i < sizeof(gssEapSaslMechs)/sizeof(gssEapSaslMechs[0]); i++) {
++        if (bufferEqual(&gssEapSaslMechs[i], name))
++            return &gssEapMechOids[i];
++    }
++
++    return GSS_C_NO_OID;
++}
+diff --git a/mech_eap/util_moonshot.c b/mech_eap/util_moonshot.c
+new file mode 100644
+index 0000000..dc0c35e
+--- /dev/null
++++ b/mech_eap/util_moonshot.c
+@@ -0,0 +1,238 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++#include "gssapiP_eap.h"
++
++#ifdef HAVE_MOONSHOT_GET_IDENTITY
++#include <libmoonshot.h>
++
++static OM_uint32
++libMoonshotMapError(OM_uint32 *minor,
++                    MoonshotError **pError)
++{
++    MoonshotError *error = *pError;
++
++    GSSEAP_ASSERT(error != NULL);
++
++    switch (error->code) {
++    case MOONSHOT_ERROR_UNABLE_TO_START_SERVICE:
++        *minor = GSSEAP_UNABLE_TO_START_IDENTITY_SERVICE;
++        break;
++    case MOONSHOT_ERROR_NO_IDENTITY_SELECTED:
++        *minor = GSSEAP_NO_IDENTITY_SELECTED;
++        break;
++    case MOONSHOT_ERROR_INSTALLATION_ERROR:
++        *minor = GSSEAP_IDENTITY_SERVICE_INSTALL_ERROR;
++        break;
++    case MOONSHOT_ERROR_OS_ERROR:
++        *minor = GSSEAP_IDENTITY_SERVICE_OS_ERROR;
++        break;
++    case MOONSHOT_ERROR_IPC_ERROR:
++        *minor = GSSEAP_IDENTITY_SERVICE_IPC_ERROR;
++        break;
++    default:
++        *minor = GSSEAP_IDENTITY_SERVICE_UNKNOWN_ERROR;
++        break;
++    }
++
++    gssEapSaveStatusInfo(*minor, error->message);
++    moonshot_error_free(error);
++    *pError = NULL;
++
++    return GSS_S_CRED_UNAVAIL;
++}
++
++OM_uint32
++libMoonshotResolveDefaultIdentity(OM_uint32 *minor,
++                                  const gss_cred_id_t cred,
++                                  gss_name_t *pName)
++{
++    OM_uint32 major, tmpMinor;
++    gss_OID nameMech = gssEapPrimaryMechForCred(cred);
++    gss_name_t name = GSS_C_NO_NAME;
++    gss_buffer_desc tmpBuffer = GSS_C_EMPTY_BUFFER;
++    char *nai = NULL;
++    char *password = NULL;
++    char *serverCertificateHash = NULL;
++    char *caCertificate = NULL;
++    char *subjectNameConstraint = NULL;
++    char *subjectAltNameConstraint = NULL;
++    MoonshotError *error = NULL;
++
++    *pName = GSS_C_NO_NAME;
++
++    if (!moonshot_get_default_identity(&nai,
++                                       &password,
++                                       &serverCertificateHash,
++                                       &caCertificate,
++                                       &subjectNameConstraint,
++                                       &subjectAltNameConstraint,
++                                       &error)) {
++        if (error->code == MOONSHOT_ERROR_NO_IDENTITY_SELECTED) {
++            major = GSS_S_CRED_UNAVAIL;
++            *minor = GSSEAP_NO_DEFAULT_IDENTITY;
++            moonshot_error_free(error);
++        } else
++            major = libMoonshotMapError(minor, &error);
++        goto cleanup;
++    }
++
++    tmpBuffer.value = nai;
++    tmpBuffer.length = strlen(nai);
++
++    major = gssEapImportName(minor, &tmpBuffer, GSS_C_NT_USER_NAME, nameMech, &name);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    *pName = name;
++    name = GSS_C_NO_NAME;
++
++cleanup:
++    moonshot_free(nai);
++    moonshot_free(password);
++    moonshot_free(serverCertificateHash);
++    moonshot_free(caCertificate);
++    moonshot_free(subjectNameConstraint);
++    moonshot_free(subjectAltNameConstraint);
++
++    gssEapReleaseName(&tmpMinor, &name);
++
++    return major;
++}
++
++OM_uint32
++libMoonshotResolveInitiatorCred(OM_uint32 *minor,
++                                gss_cred_id_t cred,
++                                const gss_name_t targetName)
++{
++    OM_uint32 major, tmpMinor;
++    gss_OID nameMech = gssEapPrimaryMechForCred(cred);
++    gss_buffer_desc initiator = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc target = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc tmpBuffer = GSS_C_EMPTY_BUFFER;
++    char *nai = NULL;
++    char *password = NULL;
++    char *serverCertificateHash = NULL;
++    char *caCertificate = NULL;
++    char *subjectNameConstraint = NULL;
++    char *subjectAltNameConstraint = NULL;
++    MoonshotError *error = NULL;
++
++    if (cred->name != GSS_C_NO_NAME) {
++        major = gssEapExportName(minor, cred->name, &initiator);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (targetName != GSS_C_NO_NAME) {
++        major = gssEapExportName(minor, targetName, &target);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++
++    if (!moonshot_get_identity((const char *)initiator.value,
++                               (const char *)cred->password.value,
++                               (const char *)target.value,
++                               &nai,
++                               &password,
++                               &serverCertificateHash,
++                               &caCertificate,
++                               &subjectNameConstraint,
++                               &subjectAltNameConstraint,
++                               &error)) {
++        major = libMoonshotMapError(minor, &error);
++        goto cleanup;
++    }
++
++    gssEapReleaseName(&tmpMinor, &cred->name);
++
++    tmpBuffer.value = nai;
++    tmpBuffer.length = strlen(nai);
++
++    major = gssEapImportName(minor, &tmpBuffer, GSS_C_NT_USER_NAME,
++                             nameMech, &cred->name);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    tmpBuffer.value = password;
++    tmpBuffer.length = strlen(password);
++
++    major = gssEapSetCredPassword(minor, cred, &tmpBuffer);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    gss_release_buffer(&tmpMinor, &cred->caCertificate);
++    gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
++    gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
++
++    if (serverCertificateHash != NULL) {
++        size_t len = strlen(serverCertificateHash);
++
++        #define HASH_PREFIX             "hash://server/sha256/"
++        #define HASH_PREFIX_LEN         (sizeof(HASH_PREFIX) - 1)
++
++        cred->caCertificate.value = GSSEAP_MALLOC(HASH_PREFIX_LEN + len + 1);
++        if (cred->caCertificate.value == NULL) {
++            major = GSS_S_FAILURE;
++            *minor = ENOMEM;
++            goto cleanup;
++        }
++
++        memcpy(cred->caCertificate.value, HASH_PREFIX, HASH_PREFIX_LEN);
++        memcpy((char *)cred->caCertificate.value + HASH_PREFIX_LEN, serverCertificateHash, len);
++
++        ((char *)cred->caCertificate.value)[HASH_PREFIX_LEN + len] = '\0';
++
++        cred->caCertificate.length = HASH_PREFIX_LEN + len;
++    } else if (caCertificate != NULL) {
++        makeStringBufferOrCleanup(caCertificate, &cred->caCertificate);
++    }
++
++    if (subjectNameConstraint != NULL)
++        makeStringBufferOrCleanup(subjectNameConstraint, &cred->subjectNameConstraint);
++    if (subjectAltNameConstraint != NULL)
++        makeStringBufferOrCleanup(subjectAltNameConstraint, &cred->subjectAltNameConstraint);
++
++cleanup:
++    moonshot_free(nai);
++    moonshot_free(password);
++    moonshot_free(serverCertificateHash);
++    moonshot_free(caCertificate);
++    moonshot_free(subjectNameConstraint);
++    moonshot_free(subjectAltNameConstraint);
++
++    gss_release_buffer(&tmpMinor, &initiator);
++    gss_release_buffer(&tmpMinor, &target);
++
++    return major;
++}
++#endif /* HAVE_MOONSHOT_GET_IDENTITY */
+diff --git a/mech_eap/util_name.c b/mech_eap/util_name.c
+new file mode 100644
+index 0000000..6045724
+--- /dev/null
++++ b/mech_eap/util_name.c
+@@ -0,0 +1,789 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Portions Copyright 2009 by the Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ */
++
++/*
++ * Name utility routines.
++ */
++
++#include "gssapiP_eap.h"
++
++static gss_OID_desc gssEapNtEapName = {
++    /* 1.3.6.1.4.1.5322.22.2.1  */
++    10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x02\x01"
++};
++
++gss_OID GSS_EAP_NT_EAP_NAME = &gssEapNtEapName;
++
++OM_uint32
++gssEapAllocName(OM_uint32 *minor, gss_name_t *pName)
++{
++    OM_uint32 tmpMinor;
++    gss_name_t name;
++
++    *pName = GSS_C_NO_NAME;
++
++    name = (gss_name_t)GSSEAP_CALLOC(1, sizeof(*name));
++    if (name == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    if (GSSEAP_MUTEX_INIT(&name->mutex) != 0) {
++        *minor = GSSEAP_GET_LAST_ERROR();
++        gssEapReleaseName(&tmpMinor, &name);
++        return GSS_S_FAILURE;
++    }
++
++    *pName = name;
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName)
++{
++    gss_name_t name;
++    krb5_context krbContext = NULL;
++    OM_uint32 tmpMinor;
++
++    *minor = 0;
++
++    if (pName == NULL) {
++        return GSS_S_COMPLETE;
++    }
++
++    name = *pName;
++    if (name == GSS_C_NO_NAME) {
++        return GSS_S_COMPLETE;
++    }
++
++    GSSEAP_KRB_INIT(&krbContext);
++    krb5_free_principal(krbContext, name->krbPrincipal);
++    gssEapReleaseOid(&tmpMinor, &name->mechanismUsed);
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    gssEapReleaseAttrContext(&tmpMinor, name);
++#endif
++
++    GSSEAP_MUTEX_DESTROY(&name->mutex);
++    GSSEAP_FREE(name);
++    *pName = NULL;
++
++    return GSS_S_COMPLETE;
++}
++
++static OM_uint32
++krbPrincipalToName(OM_uint32 *minor,
++                   krb5_principal *principal,
++                   gss_name_t *pName)
++{
++    OM_uint32 major;
++    gss_name_t name;
++
++    major = gssEapAllocName(minor, &name);
++    if (GSS_ERROR(major))
++        return major;
++
++    name->krbPrincipal = *principal;
++    *principal = NULL;
++
++    if (KRB_PRINC_LENGTH(name->krbPrincipal) > 1) {
++        name->flags |= NAME_FLAG_SERVICE;
++    } else {
++        name->flags |= NAME_FLAG_NAI;
++    }
++
++    *pName = name;
++    *minor = 0;
++
++    return GSS_S_COMPLETE;
++}
++
++static char *
++gssEapGetDefaultRealm(krb5_context krbContext)
++{
++    char *defaultRealm = NULL;
++
++    krb5_appdefault_string(krbContext, "eap_gss",
++                           NULL, "default_realm", "", &defaultRealm);
++
++    return defaultRealm;
++}
++
++static OM_uint32
++importServiceName(OM_uint32 *minor,
++                  const gss_buffer_t nameBuffer,
++                  gss_name_t *pName)
++{
++    OM_uint32 major;
++    krb5_error_code code;
++    krb5_context krbContext;
++    krb5_principal krbPrinc;
++    char *service, *host, *realm = NULL;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    major = bufferToString(minor, nameBuffer, &service);
++    if (GSS_ERROR(major))
++        return major;
++
++    host = strchr(service, '@');
++    if (host != NULL) {
++        *host = '\0';
++        host++;
++    }
++
++    realm = gssEapGetDefaultRealm(krbContext);
++
++    code = krb5_build_principal(krbContext,
++                                &krbPrinc,
++                                realm != NULL ? strlen(realm) : 0,
++                                realm != NULL ? realm : "",
++                                service,
++                                host,
++                                NULL);
++
++    if (code == 0) {
++        KRB_PRINC_TYPE(krbPrinc) = KRB5_NT_SRV_HST;
++
++        major = krbPrincipalToName(minor, &krbPrinc, pName);
++        if (GSS_ERROR(major))
++            krb5_free_principal(krbContext, krbPrinc);
++    } else {
++        major = GSS_S_FAILURE;
++        *minor = GSSEAP_BAD_SERVICE_NAME;
++    }
++
++    if (realm != NULL)
++        krb5_free_default_realm(krbContext, realm);
++    GSSEAP_FREE(service);
++
++    return major;
++}
++
++#define IMPORT_FLAG_DEFAULT_REALM           0x1
++
++/*
++ * Import an EAP name, possibly appending the default GSS EAP realm,
++ */
++static OM_uint32
++importEapNameFlags(OM_uint32 *minor,
++                   const gss_buffer_t nameBuffer,
++                   OM_uint32 importFlags,
++                   gss_name_t *pName)
++{
++    OM_uint32 major;
++    krb5_context krbContext;
++    krb5_principal krbPrinc = NULL;
++    krb5_error_code code;
++    char *nameString;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    if (nameBuffer == GSS_C_NO_BUFFER) {
++        nameString = "";
++        code = KRB5_PARSE_MALFORMED;
++    } else {
++        major = bufferToString(minor, nameBuffer, &nameString);
++        if (GSS_ERROR(major))
++            return major;
++
++        /*
++         * First, attempt to parse the name on the assumption that it includes
++         * a qualifying realm. This allows us to avoid accidentally appending
++         * the default Kerberos realm to an unqualified name. (A bug in MIT
++         * Kerberos prevents the default realm being set to an empty value.)
++         */
++        code = krb5_parse_name_flags(krbContext, nameString,
++                                     KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &krbPrinc);
++    }
++
++    if (code == KRB5_PARSE_MALFORMED) {
++        char *defaultRealm = NULL;
++        int parseFlags = 0;
++
++        /* Possibly append the default EAP realm if required */
++        if (importFlags & IMPORT_FLAG_DEFAULT_REALM)
++            defaultRealm = gssEapGetDefaultRealm(krbContext);
++
++        /* If no default realm, leave the realm empty in the parsed name */
++        if (defaultRealm == NULL || defaultRealm[0] == '\0')
++            parseFlags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
++
++        code = krb5_parse_name_flags(krbContext, nameString, parseFlags, &krbPrinc);
++
++#ifdef HAVE_HEIMDAL_VERSION
++        if (code == 0 && KRB_PRINC_REALM(krbPrinc) == NULL) {
++            KRB_PRINC_REALM(krbPrinc) = KRB_CALLOC(1, sizeof(char));
++            if (KRB_PRINC_REALM(krbPrinc) == NULL)
++                code = ENOMEM;
++        }
++#endif
++
++        if (defaultRealm != NULL)
++            krb5_free_default_realm(krbContext, defaultRealm);
++    }
++
++    if (nameBuffer != GSS_C_NO_BUFFER)
++        GSSEAP_FREE(nameString);
++
++    if (code != 0) {
++        *minor = code;
++        return GSS_S_FAILURE;
++    }
++
++    GSSEAP_ASSERT(krbPrinc != NULL);
++
++    major = krbPrincipalToName(minor, &krbPrinc, pName);
++    if (GSS_ERROR(major))
++        krb5_free_principal(krbContext, krbPrinc);
++
++    return major;
++}
++
++static OM_uint32
++importEapName(OM_uint32 *minor,
++              const gss_buffer_t nameBuffer,
++              gss_name_t *pName)
++{
++    return importEapNameFlags(minor, nameBuffer, 0, pName);
++}
++
++static OM_uint32
++importUserName(OM_uint32 *minor,
++               const gss_buffer_t nameBuffer,
++               gss_name_t *pName)
++{
++    return importEapNameFlags(minor, nameBuffer, IMPORT_FLAG_DEFAULT_REALM, pName);
++}
++
++static OM_uint32
++importAnonymousName(OM_uint32 *minor,
++                    const gss_buffer_t nameBuffer GSSEAP_UNUSED,
++                    gss_name_t *pName)
++{
++    return importEapNameFlags(minor, GSS_C_NO_BUFFER, 0, pName);
++}
++
++#define UPDATE_REMAIN(n)    do {            \
++        p += (n);                           \
++        remain -= (n);                      \
++    } while (0)
++
++#define CHECK_REMAIN(n)     do {        \
++        if (remain < (n)) {             \
++            major = GSS_S_BAD_NAME;     \
++            *minor = GSSEAP_TOK_TRUNC;  \
++            goto cleanup;               \
++        }                               \
++    } while (0)
++
++OM_uint32
++gssEapImportNameInternal(OM_uint32 *minor,
++                         const gss_buffer_t nameBuffer,
++                         gss_name_t *pName,
++                         OM_uint32 flags)
++{
++    OM_uint32 major, tmpMinor;
++    krb5_context krbContext;
++    unsigned char *p;
++    size_t len, remain;
++    gss_buffer_desc buf;
++    gss_name_t name = GSS_C_NO_NAME;
++    gss_OID mechanismUsed = GSS_C_NO_OID;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    p = (unsigned char *)nameBuffer->value;
++    remain = nameBuffer->length;
++
++    if (flags & EXPORT_NAME_FLAG_OID) {
++        gss_OID_desc mech;
++        enum gss_eap_token_type tokType;
++        uint16_t wireTokType;
++
++        /* TOK_ID || MECH_OID_LEN || MECH_OID */
++        if (remain < 6) {
++            *minor = GSSEAP_BAD_NAME_TOKEN;
++            return GSS_S_BAD_NAME;
++        }
++
++        if (flags & EXPORT_NAME_FLAG_COMPOSITE)
++            tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
++        else
++            tokType = TOK_TYPE_EXPORT_NAME;
++
++        /* TOK_ID */
++        wireTokType = load_uint16_be(p);
++
++        if ((flags & EXPORT_NAME_FLAG_ALLOW_COMPOSITE) &&
++            wireTokType == TOK_TYPE_EXPORT_NAME_COMPOSITE) {
++            tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
++            flags |= EXPORT_NAME_FLAG_COMPOSITE;
++        }
++
++        if (wireTokType != tokType) {
++            *minor = GSSEAP_WRONG_TOK_ID;
++            return GSS_S_BAD_NAME;
++        }
++        UPDATE_REMAIN(2);
++
++        /* MECH_OID_LEN */
++        len = load_uint16_be(p);
++        if (len < 2) {
++            *minor = GSSEAP_BAD_NAME_TOKEN;
++            return GSS_S_BAD_NAME;
++        }
++        UPDATE_REMAIN(2);
++
++        /* MECH_OID */
++        if (p[0] != 0x06) {
++            *minor = GSSEAP_BAD_NAME_TOKEN;
++            return GSS_S_BAD_NAME;
++        }
++
++        mech.length = p[1];
++        mech.elements = &p[2];
++
++        CHECK_REMAIN(mech.length);
++
++        major = gssEapCanonicalizeOid(minor,
++                                      &mech,
++                                      OID_FLAG_FAMILY_MECH_VALID |
++                                        OID_FLAG_MAP_FAMILY_MECH_TO_NULL,
++                                      &mechanismUsed);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        UPDATE_REMAIN(2 + mech.length);
++    }
++
++    /* NAME_LEN */
++    CHECK_REMAIN(4);
++    len = load_uint32_be(p);
++    UPDATE_REMAIN(4);
++
++    /* NAME */
++    CHECK_REMAIN(len);
++    buf.length = len;
++    buf.value = p;
++    UPDATE_REMAIN(len);
++
++    major = importEapNameFlags(minor, &buf, 0, &name);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    name->mechanismUsed = mechanismUsed;
++    mechanismUsed = GSS_C_NO_OID;
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
++        gss_buffer_desc buf;
++
++        buf.length = remain;
++        buf.value = p;
++
++        major = gssEapImportAttrContext(minor, &buf, name);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++#endif
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        gssEapReleaseOid(&tmpMinor, &mechanismUsed);
++        gssEapReleaseName(&tmpMinor, &name);
++    } else {
++        *pName = name;
++    }
++
++    return major;
++}
++
++static OM_uint32
++importExportName(OM_uint32 *minor,
++                 const gss_buffer_t nameBuffer,
++                 gss_name_t *name)
++{
++    return gssEapImportNameInternal(minor, nameBuffer, name,
++                                    EXPORT_NAME_FLAG_OID |
++                                    EXPORT_NAME_FLAG_ALLOW_COMPOSITE);
++}
++
++#ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
++static OM_uint32
++importCompositeExportName(OM_uint32 *minor,
++                          const gss_buffer_t nameBuffer,
++                          gss_name_t *name)
++{
++    return gssEapImportNameInternal(minor, nameBuffer, name,
++                                    EXPORT_NAME_FLAG_OID |
++                                    EXPORT_NAME_FLAG_COMPOSITE);
++}
++#endif
++
++struct gss_eap_name_import_provider {
++    gss_const_OID oid;
++    OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *);
++};
++
++OM_uint32
++gssEapImportName(OM_uint32 *minor,
++                 const gss_buffer_t nameBuffer,
++                 const gss_OID nameType,
++                 const gss_OID mechType,
++                 gss_name_t *pName)
++{
++    struct gss_eap_name_import_provider nameTypes[] = {
++        { GSS_EAP_NT_EAP_NAME,              importEapName               },
++        { GSS_C_NT_USER_NAME,               importUserName              },
++        { GSS_C_NT_HOSTBASED_SERVICE,       importServiceName           },
++        { GSS_C_NT_HOSTBASED_SERVICE_X,     importServiceName           },
++        { GSS_C_NT_ANONYMOUS,               importAnonymousName         },
++        { GSS_C_NT_EXPORT_NAME,             importExportName            },
++        { GSS_KRB5_NT_PRINCIPAL_NAME,       importUserName              },
++#ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
++        { GSS_C_NT_COMPOSITE_EXPORT,        importCompositeExportName   },
++#endif
++    };
++    size_t i;
++    OM_uint32 major = GSS_S_BAD_NAMETYPE;
++    OM_uint32 tmpMinor;
++    gss_name_t name = GSS_C_NO_NAME;
++
++    for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) {
++        if (oidEqual(nameTypes[i].oid,
++                     nameType == GSS_C_NO_OID ? GSS_EAP_NT_EAP_NAME : nameType)) {
++            major = nameTypes[i].import(minor, nameBuffer, &name);
++            break;
++        }
++    }
++
++    if (major == GSS_S_COMPLETE &&
++        mechType != GSS_C_NO_OID) {
++        GSSEAP_ASSERT(gssEapIsConcreteMechanismOid(mechType));
++        GSSEAP_ASSERT(name->mechanismUsed == GSS_C_NO_OID);
++
++        major = gssEapCanonicalizeOid(minor, mechType, 0, &name->mechanismUsed);
++    }
++
++    if (GSS_ERROR(major))
++        gssEapReleaseName(&tmpMinor, &name);
++    else
++        *pName = name;
++
++    return major;
++}
++
++OM_uint32
++gssEapExportName(OM_uint32 *minor,
++                 const gss_name_t name,
++                 gss_buffer_t exportedName)
++{
++    return gssEapExportNameInternal(minor, name, exportedName,
++                                    EXPORT_NAME_FLAG_OID);
++}
++
++OM_uint32
++gssEapExportNameInternal(OM_uint32 *minor,
++                         const gss_name_t name,
++                         gss_buffer_t exportedName,
++                         OM_uint32 flags)
++{
++    OM_uint32 major = GSS_S_FAILURE, tmpMinor;
++    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
++    size_t exportedNameLen;
++    unsigned char *p;
++    gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER;
++    gss_OID mech;
++
++    exportedName->length = 0;
++    exportedName->value = NULL;
++
++    if (name->mechanismUsed != GSS_C_NO_OID)
++        mech = name->mechanismUsed;
++    else
++        mech = GSS_EAP_MECHANISM;
++
++    major = gssEapDisplayName(minor, name, &nameBuf, NULL);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    exportedNameLen = 0;
++    if (flags & EXPORT_NAME_FLAG_OID) {
++        exportedNameLen += 6 + mech->length;
++    }
++    exportedNameLen += 4 + nameBuf.length;
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
++        major = gssEapExportAttrContext(minor, name, &attrs);
++        if (GSS_ERROR(major))
++            goto cleanup;
++        exportedNameLen += attrs.length;
++    }
++#endif
++
++    exportedName->value = GSSEAP_MALLOC(exportedNameLen);
++    if (exportedName->value == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++    exportedName->length = exportedNameLen;
++
++    p = (unsigned char *)exportedName->value;
++
++    if (flags & EXPORT_NAME_FLAG_OID) {
++        /* TOK | MECH_OID_LEN */
++        store_uint16_be((flags & EXPORT_NAME_FLAG_COMPOSITE)
++                        ? TOK_TYPE_EXPORT_NAME_COMPOSITE
++                        : TOK_TYPE_EXPORT_NAME,
++                        p);
++        p += 2;
++        store_uint16_be(mech->length + 2, p);
++        p += 2;
++
++        /* MECH_OID */
++        *p++ = 0x06;
++        *p++ = mech->length & 0xff;
++        memcpy(p, mech->elements, mech->length);
++        p += mech->length;
++    }
++
++    /* NAME_LEN */
++    store_uint32_be(nameBuf.length, p);
++    p += 4;
++
++    /* NAME */
++    memcpy(p, nameBuf.value, nameBuf.length);
++    p += nameBuf.length;
++
++    if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
++        memcpy(p, attrs.value, attrs.length);
++        p += attrs.length;
++    }
++
++    GSSEAP_ASSERT(p == (unsigned char *)exportedName->value + exportedNameLen);
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    gss_release_buffer(&tmpMinor, &attrs);
++    gss_release_buffer(&tmpMinor, &nameBuf);
++    if (GSS_ERROR(major))
++        gss_release_buffer(&tmpMinor, exportedName);
++
++    return major;
++}
++
++OM_uint32
++gssEapCanonicalizeName(OM_uint32 *minor,
++                       const gss_name_t input_name,
++                       const gss_OID mech_type,
++                       gss_name_t *dest_name)
++{
++    OM_uint32 major, tmpMinor;
++    krb5_context krbContext;
++    gss_name_t name;
++    gss_OID mech_used;
++
++    if (input_name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    major = gssEapAllocName(minor, &name);
++    if (GSS_ERROR(major)) {
++        return major;
++    }
++
++    if (mech_type != GSS_C_NO_OID)
++        mech_used = mech_type;
++    else
++        mech_used = input_name->mechanismUsed;
++
++    major = gssEapCanonicalizeOid(minor,
++                                  mech_used,
++                                  OID_FLAG_NULL_VALID,
++                                  &name->mechanismUsed);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    name->flags = input_name->flags;
++
++    *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal,
++                                 &name->krbPrincipal);
++    if (*minor != 0) {
++        major = GSS_S_FAILURE;
++        goto cleanup;
++    }
++
++#ifdef GSSEAP_ENABLE_ACCEPTOR
++    if (input_name->attrCtx != NULL) {
++        major = gssEapDuplicateAttrContext(minor, input_name, name);
++        if (GSS_ERROR(major))
++            goto cleanup;
++    }
++#endif
++
++    *dest_name = name;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        gssEapReleaseName(&tmpMinor, &name);
++    }
++
++    return major;
++}
++
++OM_uint32
++gssEapDuplicateName(OM_uint32 *minor,
++                    const gss_name_t input_name,
++                    gss_name_t *dest_name)
++{
++    return gssEapCanonicalizeName(minor, input_name,
++                                  GSS_C_NO_OID, dest_name);
++}
++
++OM_uint32
++gssEapDisplayName(OM_uint32 *minor,
++                  gss_name_t name,
++                  gss_buffer_t output_name_buffer,
++                  gss_OID *output_name_type)
++{
++    OM_uint32 major;
++    krb5_context krbContext;
++    char *krbName;
++    gss_OID name_type;
++    int flags = 0;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    output_name_buffer->length = 0;
++    output_name_buffer->value = NULL;
++
++    if (name == GSS_C_NO_NAME) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
++    }
++
++    /*
++     * According to draft-ietf-abfab-gss-eap-01, when the realm is
++     * absent the trailing '@' is not included.
++     */
++#ifdef HAVE_HEIMDAL_VERSION
++    if (KRB_PRINC_REALM(name->krbPrincipal) == NULL ||
++        KRB_PRINC_REALM(name->krbPrincipal)[0] == '\0')
++#else
++    if (KRB_PRINC_REALM(name->krbPrincipal)->length == 0)
++#endif
++        flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
++
++    *minor = krb5_unparse_name_flags(krbContext, name->krbPrincipal,
++                                     flags, &krbName);
++    if (*minor != 0) {
++        return GSS_S_FAILURE;
++    }
++
++    major = makeStringBuffer(minor, krbName, output_name_buffer);
++    if (GSS_ERROR(major)) {
++        krb5_free_unparsed_name(krbContext, krbName);
++        return major;
++    }
++
++    krb5_free_unparsed_name(krbContext, krbName);
++
++    if (output_name_buffer->length == 0) {
++        name_type = GSS_C_NT_ANONYMOUS;
++    } else if (name->flags & NAME_FLAG_NAI) {
++        name_type = GSS_C_NT_USER_NAME;
++    } else {
++        name_type = GSS_EAP_NT_EAP_NAME;
++    }
++
++    if (output_name_type != NULL)
++        *output_name_type = name_type;
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapCompareName(OM_uint32 *minor,
++                  gss_name_t name1,
++                  gss_name_t name2,
++                  int *name_equal)
++{
++    krb5_context krbContext;
++
++    *minor = 0;
++
++    if (name1 == GSS_C_NO_NAME && name2 == GSS_C_NO_NAME) {
++        *name_equal = 1;
++    } else if (name1 != GSS_C_NO_NAME && name2 != GSS_C_NO_NAME) {
++        GSSEAP_KRB_INIT(&krbContext);
++
++        /* krbPrincipal is immutable, so lock not required */
++        *name_equal = krb5_principal_compare(krbContext,
++                                             name1->krbPrincipal,
++                                             name2->krbPrincipal);
++    }
++
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/util_oid.c b/mech_eap/util_oid.c
+new file mode 100644
+index 0000000..096c9f8
+--- /dev/null
++++ b/mech_eap/util_oid.c
+@@ -0,0 +1,206 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 1995-2010 by the Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ *
++ */
++
++/*
++ * OID utility routines.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++duplicateOid(OM_uint32 *minor,
++             const gss_OID_desc * const oid,
++             gss_OID *newOid)
++{
++    gss_OID p;
++
++    *newOid = GSS_C_NO_OID;
++
++    p = (gss_OID)GSSEAP_MALLOC(sizeof(*p));
++    if (p == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++    p->length = oid->length;
++    p->elements = GSSEAP_MALLOC(p->length);
++    if (p->elements == NULL) {
++        GSSEAP_FREE(p);
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    memcpy(p->elements, oid->elements, p->length);
++    *newOid = p;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/* Compose an OID of a prefix and an integer suffix */
++OM_uint32
++composeOid(OM_uint32 *minor,
++           const char *prefix,
++           size_t prefix_len,
++           int suffix,
++           gss_OID_desc *oid)
++{
++    int osuffix, i;
++    size_t nbytes;
++    unsigned char *op;
++
++    if (oid == GSS_C_NO_OID) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_FAILURE;
++    }
++
++    if (oid->length < prefix_len) {
++        *minor = GSSEAP_WRONG_SIZE;
++        return GSS_S_FAILURE;
++    }
++
++    memcpy(oid->elements, prefix, prefix_len);
++
++    nbytes = 0;
++    osuffix = suffix;
++    while (suffix) {
++        nbytes++;
++        suffix >>= 7;
++    }
++    suffix = osuffix;
++
++    if (oid->length < prefix_len + nbytes) {
++        *minor = GSSEAP_WRONG_SIZE;
++        return GSS_S_FAILURE;
++    }
++
++    op = (unsigned char *) oid->elements + prefix_len + nbytes;
++    i = -1;
++    while (suffix) {
++        op[i] = (unsigned char)suffix & 0x7f;
++        if (i != -1)
++            op[i] |= 0x80;
++        i--;
++        suffix >>= 7;
++    }
++
++    oid->length = prefix_len + nbytes;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++decomposeOid(OM_uint32 *minor,
++             const char *prefix,
++             size_t prefix_len,
++             gss_OID_desc *oid,
++             int *suffix)
++{
++    size_t i, slen;
++    unsigned char *op;
++
++    if (oid->length < prefix_len ||
++        memcmp(oid->elements, prefix, prefix_len) != 0) {
++        return GSS_S_BAD_MECH;
++    }
++
++    op = (unsigned char *) oid->elements + prefix_len;
++
++    *suffix = 0;
++
++    slen = oid->length - prefix_len;
++
++    for (i = 0; i < slen; i++) {
++        *suffix = (*suffix << 7) | (op[i] & 0x7f);
++        if (i + 1 != slen && (op[i] & 0x80) == 0) {
++            *minor = GSSEAP_WRONG_SIZE;
++            return GSS_S_FAILURE;
++        }
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++duplicateOidSet(OM_uint32 *minor,
++                const gss_OID_set src,
++                gss_OID_set *dst)
++{
++    OM_uint32 major, tmpMinor;
++    int i;
++
++    if (src == GSS_C_NO_OID_SET) {
++        *dst = GSS_C_NO_OID_SET;
++        return GSS_S_COMPLETE;
++    }
++
++    major = gss_create_empty_oid_set(minor, dst);
++    if (GSS_ERROR(major))
++        return major;
++
++    for (i = 0; i < src->count; i++) {
++        gss_OID oid = &src->elements[i];
++
++        major = gss_add_oid_set_member(minor, oid, dst);
++        if (GSS_ERROR(major))
++            break;
++    }
++
++    if (GSS_ERROR(major))
++        gss_release_oid_set(&tmpMinor, dst);
++
++    return major;
++}
+diff --git a/mech_eap/util_ordering.c b/mech_eap/util_ordering.c
+new file mode 100644
+index 0000000..71ebfb5
+--- /dev/null
++++ b/mech_eap/util_ordering.c
+@@ -0,0 +1,302 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 1993 by OpenVision Technologies, Inc.
++ *
++ * Permission to use, copy, modify, distribute, and sell this software
++ * and its documentation for any purpose is hereby granted without fee,
++ * provided that the above copyright notice appears in all copies and
++ * that both that copyright notice and this permission notice appear in
++ * supporting documentation, and that the name of OpenVision not be used
++ * in advertising or publicity pertaining to distribution of the software
++ * without specific, written prior permission. OpenVision makes no
++ * representations about the suitability of this software for any
++ * purpose.  It is provided "as is" without express or implied warranty.
++ *
++ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
++ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
++ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++ * PERFORMANCE OF THIS SOFTWARE.
++ */
++
++/*
++ * Functions to check sequence numbers for replay and sequencing
++ */
++
++#include "gssapiP_eap.h"
++
++#define QUEUE_LENGTH 20
++
++typedef struct _queue {
++    int do_replay;
++    int do_sequence;
++    int start;
++    int length;
++    uint64_t firstnum;
++    /* Stored as deltas from firstnum.  This way, the high bit won't
++       overflow unless we've actually gone through 2**n messages, or
++       gotten something *way* out of sequence.  */
++    uint64_t elem[QUEUE_LENGTH];
++    /* All ones for 64-bit sequence numbers; 32 ones for 32-bit
++       sequence numbers.  */
++    uint64_t mask;
++} queue;
++
++/* rep invariant:
++ *  - the queue is a circular queue.  The first element (q->elem[q->start])
++ * is the oldest.  The last element is the newest.
++ */
++
++#define QSIZE(q) (sizeof((q)->elem)/sizeof((q)->elem[0]))
++#define QELEM(q,i) ((q)->elem[(i)%QSIZE(q)])
++
++static void
++queue_insert(queue *q, int after, uint64_t seqnum)
++{
++    /* insert.  this is not the fastest way, but it's easy, and it's
++       optimized for insert at end, which is the common case */
++    int i;
++
++    /* common case: at end, after == q->start+q->length-1 */
++
++    /* move all the elements (after,last] up one slot */
++
++    for (i = q->start + q->length - 1; i > after; i--)
++        QELEM(q,i+1) = QELEM(q,i);
++
++    /* fill in slot after+1 */
++
++    QELEM(q,after+1) = seqnum;
++
++    /* Either increase the length by one, or move the starting point up
++       one (deleting the first element, which got bashed above), as
++       appropriate. */
++
++    if (q->length == QSIZE(q)) {
++        q->start++;
++        if (q->start == QSIZE(q))
++            q->start = 0;
++    } else {
++        q->length++;
++    }
++}
++
++OM_uint32
++sequenceInit(OM_uint32 *minor,
++             void **vqueue,
++             uint64_t seqnum,
++             int do_replay,
++             int do_sequence,
++             int wide_nums)
++{
++    queue *q;
++
++    q = (queue *)GSSEAP_CALLOC(1, sizeof(queue));
++    if (q == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    q->do_replay = do_replay;
++    q->do_sequence = do_sequence;
++    q->mask = wide_nums ? ~(uint64_t)0 : 0xffffffffUL;
++
++    q->start = 0;
++    q->length = 1;
++    q->firstnum = seqnum;
++    q->elem[q->start] = ((uint64_t)0 - 1) & q->mask;
++
++    *vqueue = (void *)q;
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++sequenceCheck(OM_uint32 *minor,
++              void **vqueue,
++              uint64_t seqnum)
++{
++    queue *q;
++    int i;
++    uint64_t expected;
++
++    *minor = 0;
++
++    q = (queue *) (*vqueue);
++
++    if (!q->do_replay && !q->do_sequence)
++        return GSS_S_COMPLETE;
++
++    /* All checks are done relative to the initial sequence number, to
++       avoid (or at least put off) the pain of wrapping.  */
++    seqnum -= q->firstnum;
++    /* If we're only doing 32-bit values, adjust for that again.
++
++       Note that this will probably be the wrong thing to if we get
++       2**32 messages sent with 32-bit sequence numbers.  */
++    seqnum &= q->mask;
++
++    /* rule 1: expected sequence number */
++
++    expected = (QELEM(q,q->start+q->length-1)+1) & q->mask;
++    if (seqnum == expected) {
++        queue_insert(q, q->start+q->length-1, seqnum);
++        return GSS_S_COMPLETE;
++    }
++
++    /* rule 2: > expected sequence number */
++
++    if ((seqnum > expected)) {
++        queue_insert(q, q->start+q->length-1, seqnum);
++        if (q->do_replay && !q->do_sequence)
++            return GSS_S_COMPLETE;
++        else
++            return GSS_S_GAP_TOKEN;
++    }
++
++    /* rule 3: seqnum < seqnum(first) */
++
++    if ((seqnum < QELEM(q,q->start)) &&
++        /* Is top bit of whatever width we're using set?
++
++           We used to check for greater than or equal to firstnum, but
++           (1) we've since switched to compute values relative to
++           firstnum, so the lowest we can have is 0, and (2) the effect
++           of the original scheme was highly dependent on whether
++           firstnum was close to either side of 0.  (Consider
++           firstnum==0xFFFFFFFE and we miss three packets; the next
++           packet is *new* but would look old.)
++
++           This check should give us 2**31 or 2**63 messages "new", and
++           just as many "old".  That's not quite right either.  */
++        (seqnum & (1 + (q->mask >> 1)))
++    ) {
++        if (q->do_replay && !q->do_sequence)
++            return GSS_S_OLD_TOKEN;
++        else
++            return GSS_S_UNSEQ_TOKEN;
++    }
++
++    /* rule 4+5: seqnum in [seqnum(first),seqnum(last)]  */
++
++    else {
++        if (seqnum == QELEM(q,q->start+q->length - 1))
++            return GSS_S_DUPLICATE_TOKEN;
++
++        for (i = q->start; i < q->start + q->length - 1; i++) {
++            if (seqnum == QELEM(q,i))
++                return GSS_S_DUPLICATE_TOKEN;
++            if ((seqnum > QELEM(q,i)) && (seqnum < QELEM(q,i+1))) {
++                queue_insert(q, i, seqnum);
++                if (q->do_replay && !q->do_sequence)
++                    return GSS_S_COMPLETE;
++                else
++                    return GSS_S_UNSEQ_TOKEN;
++            }
++        }
++    }
++
++    /* this should never happen */
++    return GSS_S_FAILURE;
++}
++
++OM_uint32
++sequenceFree(OM_uint32 *minor, void **vqueue)
++{
++    queue *q;
++
++    q = (queue *) (*vqueue);
++
++    GSSEAP_FREE(q);
++
++    *vqueue = NULL;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++/*
++ * These support functions are for the serialization routines
++ */
++size_t
++sequenceSize(void *vqueue GSSEAP_UNUSED)
++{
++    return sizeof(queue);
++}
++
++OM_uint32
++sequenceExternalize(OM_uint32 *minor,
++                    void *vqueue,
++                    unsigned char **buf,
++                    size_t *lenremain)
++{
++    if (*lenremain < sizeof(queue)) {
++        *minor = GSSEAP_WRONG_SIZE;
++        return GSS_S_FAILURE;
++    }
++    memcpy(*buf, vqueue, sizeof(queue));
++    *buf += sizeof(queue);
++    *lenremain -= sizeof(queue);
++
++    return 0;
++}
++
++OM_uint32
++sequenceInternalize(OM_uint32 *minor,
++                    void **vqueue,
++                    unsigned char **buf,
++                    size_t *lenremain)
++{
++    void *q;
++
++    if (*lenremain < sizeof(queue)) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_DEFECTIVE_TOKEN;
++    }
++
++    q = GSSEAP_MALLOC(sizeof(queue));
++    if (q == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    memcpy(q, *buf, sizeof(queue));
++    *buf += sizeof(queue);
++    *lenremain -= sizeof(queue);
++    *vqueue = q;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/util_radius.cpp b/mech_eap/util_radius.cpp
+new file mode 100644
+index 0000000..9111e20
+--- /dev/null
++++ b/mech_eap/util_radius.cpp
+@@ -0,0 +1,899 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * RADIUS attribute provider implementation.
++ */
++
++#include "gssapiP_eap.h"
++
++/* stuff that should be provided by libradsec/libfreeradius-radius */
++#define VENDORATTR(vendor, attr)            (((vendor) << 16) | (attr))
++
++#ifndef ATTRID
++#define ATTRID(attr)                        ((attr) & 0xFFFF)
++#endif
++
++static gss_buffer_desc radiusUrnPrefix = {
++    sizeof("urn:x-radius:") - 1,
++    (void *)"urn:x-radius:"
++};
++
++static VALUE_PAIR *copyAvps(const VALUE_PAIR *src);
++
++gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
++{
++    m_vps = NULL;
++    m_authenticated = false;
++}
++
++gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
++{
++    if (m_vps != NULL)
++        pairfree(&m_vps);
++}
++
++bool
++gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
++                                                      const gss_eap_attr_provider *ctx)
++{
++    const gss_eap_radius_attr_provider *radius;
++
++    if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
++        return false;
++
++    radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
++
++    if (radius->m_vps != NULL)
++        m_vps = copyAvps(const_cast<VALUE_PAIR *>(radius->getAvps()));
++
++    m_authenticated = radius->m_authenticated;
++
++    return true;
++}
++
++bool
++gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
++                                                 const gss_cred_id_t gssCred,
++                                                 const gss_ctx_id_t gssCtx)
++{
++    if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
++        return false;
++
++    if (gssCtx != GSS_C_NO_CONTEXT) {
++        if (gssCtx->acceptorCtx.vps != NULL) {
++            m_vps = copyAvps(gssCtx->acceptorCtx.vps);
++            if (m_vps == NULL)
++                return false;
++
++            /* We assume libradsec validated this for us */
++            GSSEAP_ASSERT(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL);
++            m_authenticated = true;
++        }
++    }
++
++    return true;
++}
++
++static bool
++alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
++{
++    for (std::vector<std::string>::const_iterator a = attrs.begin();
++         a != attrs.end();
++         ++a) {
++        if (strcmp(vp->name, (*a).c_str()) == 0)
++            return true;
++    }
++
++    return false;
++}
++
++static bool
++isSecretAttributeP(uint16_t attrid, uint16_t vendor)
++{
++    bool bSecretAttribute = false;
++
++    switch (vendor) {
++    case VENDORPEC_MS:
++        switch (attrid) {
++        case PW_MS_MPPE_SEND_KEY:
++        case PW_MS_MPPE_RECV_KEY:
++            bSecretAttribute = true;
++            break;
++        default:
++            break;
++        }
++    default:
++        break;
++    }
++
++    return bSecretAttribute;
++}
++
++static bool
++isSecretAttributeP(uint32_t attribute)
++{
++    return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute));
++}
++
++static bool
++isInternalAttributeP(uint16_t attrid, uint16_t vendor)
++{
++    bool bInternalAttribute = false;
++
++    /* should have been filtered */
++    GSSEAP_ASSERT(!isSecretAttributeP(attrid, vendor));
++
++    switch (vendor) {
++    case VENDORPEC_UKERNA:
++        switch (attrid) {
++        case PW_GSS_ACCEPTOR_SERVICE_NAME:
++        case PW_GSS_ACCEPTOR_HOST_NAME:
++        case PW_GSS_ACCEPTOR_SERVICE_SPECIFIC:
++        case PW_GSS_ACCEPTOR_REALM_NAME:
++        case PW_SAML_AAA_ASSERTION:
++            bInternalAttribute = true;
++            break;
++        default:
++            break;
++        }
++        break;
++    default:
++        break;
++    }
++
++    return bInternalAttribute;
++}
++
++static bool
++isInternalAttributeP(uint32_t attribute)
++{
++    return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute));
++}
++
++static bool
++isFragmentedAttributeP(uint16_t attrid, uint16_t vendor)
++{
++    /* A bit of a hack for the PAC for now. Should be configurable. */
++    return (vendor == VENDORPEC_UKERNA) &&
++        !isInternalAttributeP(attrid, vendor);
++}
++
++static bool
++isFragmentedAttributeP(uint32_t attribute)
++{
++    return isFragmentedAttributeP(ATTRID(attribute), VENDOR(attribute));
++}
++
++/*
++ * Copy AVP list, same as paircopy except it filters out attributes
++ * containing keys.
++ */
++static VALUE_PAIR *
++copyAvps(const VALUE_PAIR *src)
++{
++    const VALUE_PAIR *vp;
++    VALUE_PAIR *dst = NULL, **pDst = &dst;
++
++    for (vp = src; vp != NULL; vp = vp->next) {
++        VALUE_PAIR *vpcopy;
++
++        if (isSecretAttributeP(vp->attribute))
++            continue;
++
++        vpcopy = paircopyvp(vp);
++        if (vpcopy == NULL) {
++            pairfree(&dst);
++            throw std::bad_alloc();
++        }
++        *pDst = vpcopy;
++        pDst = &vpcopy->next;
++     }
++
++    return dst;
++}
++
++bool
++gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
++                                                void *data) const
++{
++    VALUE_PAIR *vp;
++    std::vector <std::string> seen;
++
++    for (vp = m_vps; vp != NULL; vp = vp->next) {
++        gss_buffer_desc attribute;
++        char attrid[64];
++
++        /* Don't advertise attributes that are internal to the GSS-EAP mechanism */
++        if (isInternalAttributeP(vp->attribute))
++            continue;
++
++        if (alreadyAddedAttributeP(seen, vp))
++            continue;
++
++        snprintf(attrid, sizeof(attrid), "%s%d",
++            (char *)radiusUrnPrefix.value, vp->attribute);
++
++        attribute.value = attrid;
++        attribute.length = strlen(attrid);
++
++        if (!addAttribute(m_manager, this, &attribute, data))
++            return false;
++
++        seen.push_back(std::string(vp->name));
++    }
++
++    return true;
++}
++
++uint32_t
++getAttributeId(const gss_buffer_t attr)
++{
++    OM_uint32 tmpMinor;
++    gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
++    DICT_ATTR *da;
++    char *s;
++    uint32_t attrid = 0;
++
++    if (attr->length < radiusUrnPrefix.length ||
++        memcmp(attr->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
++        return 0;
++
++    /* need to duplicate because attr may not be NUL terminated */
++    duplicateBuffer(*attr, &strAttr);
++    s = (char *)strAttr.value + radiusUrnPrefix.length;
++
++    if (isdigit(*s)) {
++        attrid = strtoul(s, NULL, 10);
++    } else {
++        da = dict_attrbyname(s);
++        if (da != NULL)
++            attrid = da->attr;
++    }
++
++    gss_release_buffer(&tmpMinor, &strAttr);
++
++    return attrid;
++}
++
++bool
++gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
++                                           uint32_t attrid,
++                                           const gss_buffer_t value)
++{
++    OM_uint32 major = GSS_S_UNAVAILABLE, minor;
++
++    if (!isSecretAttributeP(attrid) &&
++        !isInternalAttributeP(attrid)) {
++        deleteAttribute(attrid);
++
++        major = gssEapRadiusAddAvp(&minor, &m_vps,
++                                   ATTRID(attrid), VENDOR(attrid), 
++                                   value);
++    }
++
++    return !GSS_ERROR(major);
++}
++
++bool
++gss_eap_radius_attr_provider::setAttribute(int complete,
++                                           const gss_buffer_t attr,
++                                           const gss_buffer_t value)
++{
++    uint32_t attrid = getAttributeId(attr);
++
++    if (!attrid)
++        return false;
++
++    return setAttribute(complete, attrid, value);
++}
++
++bool
++gss_eap_radius_attr_provider::deleteAttribute(uint32_t attrid)
++{
++    if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid) ||
++        pairfind(m_vps, attrid) == NULL)
++        return false;
++
++    pairdelete(&m_vps, attrid);
++
++    return true;
++}
++
++bool
++gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
++{
++    uint32_t attrid = getAttributeId(attr);
++
++    if (!attrid)
++        return false;
++
++    return deleteAttribute(attrid);
++}
++
++bool
++gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
++                                           int *authenticated,
++                                           int *complete,
++                                           gss_buffer_t value,
++                                           gss_buffer_t display_value,
++                                           int *more) const
++{
++    uint32_t attrid;
++
++    attrid = getAttributeId(attr);
++    if (!attrid)
++        return false;
++
++    return getAttribute(attrid, authenticated, complete,
++                        value, display_value, more);
++}
++
++bool
++gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
++                                           int *authenticated,
++                                           int *complete,
++                                           gss_buffer_t value,
++                                           gss_buffer_t display_value,
++                                           int *more) const
++{
++    VALUE_PAIR *vp;
++    int i = *more, count = 0;
++
++    *more = 0;
++
++    if (i == -1)
++        i = 0;
++
++    if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid)) {
++        return false;
++    } else if (isFragmentedAttributeP(attrid)) {
++        return getFragmentedAttribute(attrid,
++                                      authenticated,
++                                      complete,
++                                      value);
++    }
++
++    for (vp = pairfind(m_vps, attrid);
++         vp != NULL;
++         vp = pairfind(vp->next, attrid)) {
++        if (count++ == i) {
++            if (pairfind(vp->next, attrid) != NULL)
++                *more = count;
++            break;
++        }
++    }
++
++    if (vp == NULL && *more == 0)
++        return false;
++
++    if (value != GSS_C_NO_BUFFER) {
++        gss_buffer_desc valueBuf;
++
++        valueBuf.value = (void *)vp->vp_octets;
++        valueBuf.length = vp->length;
++
++        duplicateBuffer(valueBuf, value);
++    }
++
++    if (display_value != GSS_C_NO_BUFFER &&
++        vp->type != PW_TYPE_OCTETS) {
++        char displayString[MAX_STRING_LEN];
++        gss_buffer_desc displayBuf;
++
++        displayBuf.length = vp_prints_value(displayString,
++                                            sizeof(displayString), vp, 0);
++        displayBuf.value = (void *)displayString;
++
++        duplicateBuffer(displayBuf, display_value);
++    }
++
++    if (authenticated != NULL)
++        *authenticated = m_authenticated;
++    if (complete != NULL)
++        *complete = true;
++
++    return true;
++}
++
++bool
++gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute,
++                                                     uint16_t vendor,
++                                                     int *authenticated,
++                                                     int *complete,
++                                                     gss_buffer_t value) const
++{
++    OM_uint32 major, minor;
++
++    major = gssEapRadiusGetAvp(&minor, m_vps, attribute, vendor, value, TRUE);
++
++    if (authenticated != NULL)
++        *authenticated = m_authenticated;
++    if (complete != NULL)
++        *complete = true;
++
++    return !GSS_ERROR(major);
++}
++
++bool
++gss_eap_radius_attr_provider::getFragmentedAttribute(uint32_t attrid,
++                                                     int *authenticated,
++                                                     int *complete,
++                                                     gss_buffer_t value) const
++{
++    return getFragmentedAttribute(ATTRID(attrid), VENDOR(attrid),
++                                  authenticated, complete, value);
++}
++
++bool
++gss_eap_radius_attr_provider::getAttribute(uint16_t attribute,
++                                           uint16_t vendor,
++                                           int *authenticated,
++                                           int *complete,
++                                           gss_buffer_t value,
++                                           gss_buffer_t display_value,
++                                           int *more) const
++{
++
++    return getAttribute(VENDORATTR(attribute, vendor),
++                        authenticated, complete,
++                        value, display_value, more);
++}
++
++gss_any_t
++gss_eap_radius_attr_provider::mapToAny(int authenticated,
++                                       gss_buffer_t type_id GSSEAP_UNUSED) const
++{
++    if (authenticated && !m_authenticated)
++        return (gss_any_t)NULL;
++
++    return (gss_any_t)copyAvps(m_vps);
++}
++
++void
++gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
++                                                    gss_any_t input) const
++{
++    VALUE_PAIR *vp = (VALUE_PAIR *)input;
++    pairfree(&vp);
++}
++
++bool
++gss_eap_radius_attr_provider::init(void)
++{
++    gss_eap_attr_ctx::registerProvider(ATTR_TYPE_RADIUS, createAttrContext);
++
++    return true;
++}
++
++void
++gss_eap_radius_attr_provider::finalize(void)
++{
++    gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_RADIUS);
++}
++
++gss_eap_attr_provider *
++gss_eap_radius_attr_provider::createAttrContext(void)
++{
++    return new gss_eap_radius_attr_provider;
++}
++
++OM_uint32
++gssEapRadiusAddAvp(OM_uint32 *minor,
++                   VALUE_PAIR **vps,
++                   uint16_t attribute,
++                   uint16_t vendor,
++                   const gss_buffer_t buffer)
++{
++    uint32_t attrid = VENDORATTR(vendor, attribute);
++    unsigned char *p = (unsigned char *)buffer->value;
++    size_t remain = buffer->length;
++
++    do {
++        VALUE_PAIR *vp;
++        size_t n = remain;
++
++        /*
++         * There's an extra byte of padding; RADIUS AVPs can only
++         * be 253 octets.
++         */
++        if (n >= MAX_STRING_LEN)
++            n = MAX_STRING_LEN - 1;
++
++        vp = paircreate(attrid, PW_TYPE_OCTETS);
++        if (vp == NULL) {
++            *minor = ENOMEM;
++            return GSS_S_FAILURE;
++        }
++
++        memcpy(vp->vp_octets, p, n);
++        vp->length = n;
++
++        pairadd(vps, vp);
++
++        p += n;
++        remain -= n;
++    } while (remain != 0);
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapRadiusGetRawAvp(OM_uint32 *minor,
++                      VALUE_PAIR *vps,
++                      uint16_t attribute,
++                      uint16_t vendor,
++                      VALUE_PAIR **vp)
++{
++    uint32_t attr = VENDORATTR(vendor, attribute);
++
++    *vp = pairfind(vps, attr);
++    if (*vp == NULL) {
++        *minor = GSSEAP_NO_SUCH_ATTR;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapRadiusGetAvp(OM_uint32 *minor,
++                   VALUE_PAIR *vps,
++                   uint16_t attribute,
++                   uint16_t vendor,
++                   gss_buffer_t buffer,
++                   int concat)
++{
++    VALUE_PAIR *vp;
++    unsigned char *p;
++    uint32_t attr = VENDORATTR(vendor, attribute);
++
++    if (buffer != GSS_C_NO_BUFFER) {
++        buffer->length = 0;
++        buffer->value = NULL;
++    }
++
++    vp = pairfind(vps, attr);
++    if (vp == NULL) {
++        *minor = GSSEAP_NO_SUCH_ATTR;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (buffer != GSS_C_NO_BUFFER) {
++        do {
++            buffer->length += vp->length;
++        } while (concat && (vp = pairfind(vp->next, attr)) != NULL);
++
++        buffer->value = GSSEAP_MALLOC(buffer->length);
++        if (buffer->value == NULL) {
++            *minor = ENOMEM;
++            return GSS_S_FAILURE;
++        }
++
++        p = (unsigned char *)buffer->value;
++
++        for (vp = pairfind(vps, attr);
++             concat && vp != NULL;
++             vp = pairfind(vp->next, attr)) {
++            memcpy(p, vp->vp_octets, vp->length);
++            p += vp->length;
++        }
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapRadiusFreeAvps(OM_uint32 *minor,
++                     VALUE_PAIR **vps)
++{
++    pairfree(vps);
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapRadiusAttrProviderInit(OM_uint32 *minor)
++{
++    if (!gss_eap_radius_attr_provider::init()) {
++        *minor = GSSEAP_RADSEC_INIT_FAILURE;
++        return GSS_S_FAILURE;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
++{
++    gss_eap_radius_attr_provider::finalize();
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++static JSONObject
++avpToJson(const VALUE_PAIR *vp)
++{
++    JSONObject obj;
++
++    GSSEAP_ASSERT(vp->length <= MAX_STRING_LEN);
++
++    switch (vp->type) {
++    case PW_TYPE_INTEGER:
++    case PW_TYPE_IPADDR:
++    case PW_TYPE_DATE:
++        obj.set("value", vp->lvalue);
++        break;
++    case PW_TYPE_STRING:
++        obj.set("value", vp->vp_strvalue);
++        break;
++    default: {
++        char *b64;
++
++        if (base64Encode(vp->vp_octets, vp->length, &b64) < 0)
++            throw std::bad_alloc();
++
++        obj.set("value", b64);
++        GSSEAP_FREE(b64);
++        break;
++    }
++    }
++
++    obj.set("type", vp->attribute);
++
++    return obj;
++}
++
++static bool
++jsonToAvp(VALUE_PAIR **pVp, JSONObject &obj)
++{
++    VALUE_PAIR *vp = NULL;
++    DICT_ATTR *da;
++    uint32_t attrid;
++
++    JSONObject type = obj["type"];
++    JSONObject value = obj["value"];
++
++    if (!type.isInteger())
++        goto fail;
++
++    attrid = type.integer();
++    da = dict_attrbyvalue(attrid);
++    if (da != NULL) {
++        vp = pairalloc(da);
++    } else {
++        int type = base64Valid(value.string()) ?
++            PW_TYPE_OCTETS : PW_TYPE_STRING;
++        vp = paircreate(attrid, type);
++    }
++    if (vp == NULL)
++        throw std::bad_alloc();
++
++    switch (vp->type) {
++    case PW_TYPE_INTEGER:
++    case PW_TYPE_IPADDR:
++    case PW_TYPE_DATE:
++        if (!value.isInteger())
++            goto fail;
++
++        vp->length = 4;
++        vp->lvalue = value.integer();
++        break;
++    case PW_TYPE_STRING: {
++        if (!value.isString())
++            goto fail;
++
++        const char *str = value.string();
++        size_t len = strlen(str);
++
++        if (len >= MAX_STRING_LEN)
++            goto fail;
++
++        vp->length = len;
++        memcpy(vp->vp_strvalue, str, len + 1);
++        break;
++    }
++    case PW_TYPE_OCTETS:
++    default: {
++        if (!value.isString())
++            goto fail;
++
++        const char *str = value.string();
++        ssize_t len = strlen(str);
++
++        /* this optimization requires base64Decode only understand packed encoding */
++        if (len >= BASE64_EXPAND(MAX_STRING_LEN))
++            goto fail;
++
++        len = base64Decode(str, vp->vp_octets);
++        if (len < 0)
++            goto fail;
++
++        vp->length = len;
++        break;
++    }
++    }
++
++    *pVp = vp;
++
++    return true;
++
++fail:
++    if (vp != NULL)
++        pairbasicfree(vp);
++    *pVp = NULL;
++    return false;
++}
++
++const char *
++gss_eap_radius_attr_provider::name(void) const
++{
++    return "radius";
++}
++
++bool
++gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
++                                                 JSONObject &obj)
++{
++    VALUE_PAIR **pNext = &m_vps;
++
++    if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
++        return false;
++
++    JSONObject attrs = obj["attributes"];
++    size_t nelems = attrs.size();
++
++    for (size_t i = 0; i < nelems; i++) {
++        JSONObject attr = attrs[i];
++        VALUE_PAIR *vp;
++
++        if (!jsonToAvp(&vp, attr))
++            return false;
++
++        *pNext = vp;
++        pNext = &vp->next;
++    }
++
++    m_authenticated = obj["authenticated"].integer() ? true : false;
++
++    return true;
++}
++
++const char *
++gss_eap_radius_attr_provider::prefix(void) const
++{
++    return "urn:ietf:params:gss-eap:radius-avp";
++}
++
++JSONObject
++gss_eap_radius_attr_provider::jsonRepresentation(void) const
++{
++    JSONObject obj, attrs = JSONObject::array();
++
++    for (VALUE_PAIR *vp = m_vps; vp != NULL; vp = vp->next) {
++        JSONObject attr = avpToJson(vp);
++        attrs.append(attr);
++    }
++
++    obj.set("attributes", attrs);
++
++    obj.set("authenticated", m_authenticated);
++
++    return obj;
++}
++
++time_t
++gss_eap_radius_attr_provider::getExpiryTime(void) const
++{
++    VALUE_PAIR *vp;
++
++    vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
++    if (vp == NULL || vp->lvalue == 0)
++        return 0;
++
++    return time(NULL) + vp->lvalue;
++}
++
++OM_uint32
++gssEapRadiusMapError(OM_uint32 *minor,
++                     struct rs_error *err)
++{
++    int code;
++
++    GSSEAP_ASSERT(err != NULL);
++
++    code = rs_err_code(err, 0);
++
++    if (code == RSE_OK) {
++        *minor = 0;
++        return GSS_S_COMPLETE;
++    }
++
++    *minor = ERROR_TABLE_BASE_rse + code;
++
++    gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
++    rs_err_free(err);
++
++    return GSS_S_FAILURE;
++}
++
++OM_uint32
++gssEapCreateRadiusContext(OM_uint32 *minor,
++                          gss_cred_id_t cred,
++                          struct rs_context **pRadContext)
++{
++    const char *configFile = RS_CONFIG_FILE;
++    struct rs_context *radContext;
++    struct rs_alloc_scheme ralloc;
++    struct rs_error *err;
++    OM_uint32 major;
++
++    *pRadContext = NULL;
++
++    if (rs_context_create(&radContext) != 0) {
++        *minor = GSSEAP_RADSEC_CONTEXT_FAILURE;
++        return GSS_S_FAILURE;
++    }
++
++    if (cred->radiusConfigFile.value != NULL)
++        configFile = (const char *)cred->radiusConfigFile.value;
++
++    ralloc.calloc  = GSSEAP_CALLOC;
++    ralloc.malloc  = GSSEAP_MALLOC;
++    ralloc.free    = GSSEAP_FREE;
++    ralloc.realloc = GSSEAP_REALLOC;
++
++    rs_context_set_alloc_scheme(radContext, &ralloc);
++
++    if (rs_context_read_config(radContext, configFile) != 0) {
++        err = rs_err_ctx_pop(radContext);
++        goto fail;
++    }
++
++    if (rs_context_init_freeradius_dict(radContext, NULL) != 0) {
++        err = rs_err_ctx_pop(radContext);
++        goto fail;
++    }
++
++    *pRadContext = radContext;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++
++fail:
++    major = gssEapRadiusMapError(minor, err);
++    rs_context_destroy(radContext);
++
++    return major;
++}
+diff --git a/mech_eap/util_radius.h b/mech_eap/util_radius.h
+new file mode 100644
+index 0000000..481876a
+--- /dev/null
++++ b/mech_eap/util_radius.h
+@@ -0,0 +1,183 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * RADIUS attribute provider.
++ */
++
++#ifndef _UTIL_RADIUS_H_
++#define _UTIL_RADIUS_H_ 1
++
++#ifdef __cplusplus
++
++struct gss_eap_radius_attr_provider : gss_eap_attr_provider {
++public:
++    gss_eap_radius_attr_provider(void);
++    ~gss_eap_radius_attr_provider(void);
++
++    bool initWithExistingContext(const gss_eap_attr_ctx *source,
++                                 const gss_eap_attr_provider *ctx);
++    bool initWithGssContext(const gss_eap_attr_ctx *source,
++                            const gss_cred_id_t cred,
++                            const gss_ctx_id_t ctx);
++
++    bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const;
++    bool setAttribute(int complete,
++                      const gss_buffer_t attr,
++                      const gss_buffer_t value);
++    bool deleteAttribute(const gss_buffer_t attr);
++    bool getAttribute(const gss_buffer_t attr,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    gss_any_t mapToAny(int authenticated,
++                       gss_buffer_t type_id) const;
++    void releaseAnyNameMapping(gss_buffer_t type_id,
++                               gss_any_t input) const;
++
++    const char *prefix(void) const;
++    const char *name(void) const;
++    bool initWithJsonObject(const gss_eap_attr_ctx *manager,
++                           JSONObject &obj);
++    JSONObject jsonRepresentation(void) const;
++
++    bool getAttribute(uint32_t attribute,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    bool getAttribute(uint16_t attribute,
++                      uint16_t vendor,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    bool setAttribute(int complete,
++                      uint32_t attribute,
++                      const gss_buffer_t value);
++    bool deleteAttribute(uint32_t attribute);
++
++    bool getFragmentedAttribute(uint16_t attribute,
++                                uint16_t vendor,
++                                int *authenticated,
++                                int *complete,
++                                gss_buffer_t value) const;
++    bool getFragmentedAttribute(uint32_t attrid,
++                                int *authenticated,
++                                int *complete,
++                                gss_buffer_t value) const;
++
++    bool authenticated(void) const { return m_authenticated; }
++
++    time_t getExpiryTime(void) const;
++
++    static bool init(void);
++    static void finalize(void);
++
++    static gss_eap_attr_provider *createAttrContext(void);
++
++private:
++    const VALUE_PAIR *getAvps(void) const {
++        return m_vps;
++    }
++
++    VALUE_PAIR *m_vps;
++    bool m_authenticated;
++};
++
++/* For now */
++extern "C" {
++#endif
++
++OM_uint32
++gssEapRadiusAddAvp(OM_uint32 *minor,
++                   VALUE_PAIR **vp,
++                   uint16_t type,
++                   uint16_t vendor,
++                   const gss_buffer_t buffer);
++
++OM_uint32
++gssEapRadiusGetAvp(OM_uint32 *minor,
++                   VALUE_PAIR *vps,
++                   uint16_t type,
++                   uint16_t vendor,
++                   gss_buffer_t buffer,
++                   int concat);
++
++OM_uint32
++gssEapRadiusGetRawAvp(OM_uint32 *minor,
++                      VALUE_PAIR *vps,
++                      uint16_t type,
++                      uint16_t vendor,
++                      VALUE_PAIR **vp);
++OM_uint32
++gssEapRadiusFreeAvps(OM_uint32 *minor,
++                     VALUE_PAIR **vps);
++
++OM_uint32 gssEapRadiusAttrProviderInit(OM_uint32 *minor);
++OM_uint32 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor);
++
++OM_uint32
++gssEapRadiusMapError(OM_uint32 *minor,
++                     struct rs_error *err);
++
++OM_uint32
++gssEapCreateRadiusContext(OM_uint32 *minor,
++                          gss_cred_id_t cred,
++                          struct rs_context **pRadContext);
++
++/* This really needs to be a function call on Windows */
++#define RS_CONFIG_FILE      SYSCONFDIR "/radsec.conf"
++
++#define VENDORPEC_MS                        311 /* RFC 2548 */
++
++#define PW_MS_MPPE_SEND_KEY                 16
++#define PW_MS_MPPE_RECV_KEY                 17
++
++#define VENDORPEC_UKERNA                    25622
++
++#define PW_GSS_ACCEPTOR_SERVICE_NAME        128
++#define PW_GSS_ACCEPTOR_HOST_NAME           129
++#define PW_GSS_ACCEPTOR_SERVICE_SPECIFIC    130
++#define PW_GSS_ACCEPTOR_REALM_NAME          131
++#define PW_SAML_AAA_ASSERTION               132
++#define PW_MS_WINDOWS_AUTH_DATA             133
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _UTIL_RADIUS_H_ */
+diff --git a/mech_eap/util_reauth.c b/mech_eap/util_reauth.c
+new file mode 100644
+index 0000000..50011ca
+--- /dev/null
++++ b/mech_eap/util_reauth.c
+@@ -0,0 +1,1196 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Fast reauthentication support.
++ */
++
++#include "gssapiP_eap.h"
++
++#include <dlfcn.h>
++
++/*
++ * Fast reauthentication support for EAP GSS.
++ */
++
++krb5_error_code
++krb5_encrypt_tkt_part(krb5_context, const krb5_keyblock *, krb5_ticket *);
++
++krb5_error_code
++encode_krb5_ticket(const krb5_ticket *rep, krb5_data **code);
++
++static OM_uint32
++gssDisplayName(OM_uint32 *minor,
++               gss_name_t name,
++               gss_buffer_t buffer,
++               gss_OID *name_type);
++
++static OM_uint32
++gssImportName(OM_uint32 *minor,
++              gss_buffer_t buffer,
++              gss_OID name_type,
++              gss_name_t *name);
++
++static krb5_error_code
++getAcceptorKey(krb5_context krbContext,
++               gss_ctx_id_t ctx,
++               gss_cred_id_t cred,
++               krb5_principal *princ,
++               krb5_keyblock *key)
++{
++    krb5_error_code code;
++    krb5_keytab keytab = NULL;
++    krb5_keytab_entry ktent = { 0 };
++    krb5_kt_cursor cursor;
++
++    *princ = NULL;
++    memset(key, 0, sizeof(*key));
++    memset(&cursor, 0, sizeof(cursor));
++
++    code = krb5_kt_default(krbContext, &keytab);
++    if (code != 0)
++        goto cleanup;
++
++    if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
++        code = krb5_kt_get_entry(krbContext, keytab,
++                                 cred->name->krbPrincipal, 0,
++                                 ctx->encryptionType, &ktent);
++        if (code != 0)
++            goto cleanup;
++    } else {
++        /*
++         * It's not clear that looking encrypting the ticket in the
++         * requested EAP enctype provides any value.
++         */
++        code = krb5_kt_start_seq_get(krbContext, keytab, &cursor);
++        if (code != 0)
++            goto cleanup;
++
++        while ((code = krb5_kt_next_entry(krbContext, keytab,
++                                          &ktent, &cursor)) == 0) {
++            if (KRB_KEY_TYPE(KRB_KT_ENT_KEYBLOCK(&ktent)) == ctx->encryptionType)
++                break;
++            else
++                KRB_KT_ENT_FREE(krbContext, &ktent);
++        }
++    }
++
++    if (code == 0) {
++        *princ = ktent.principal;
++        *key = *KRB_KT_ENT_KEYBLOCK(&ktent);
++    }
++
++cleanup:
++    if (cred == GSS_C_NO_CREDENTIAL || cred->name == GSS_C_NO_NAME)
++        krb5_kt_end_seq_get(krbContext, keytab, &cursor);
++    krb5_kt_close(krbContext, keytab);
++    if (code != 0)
++        KRB_KT_ENT_FREE(krbContext, &ktent);
++
++    return code;
++}
++
++static OM_uint32
++freezeAttrContext(OM_uint32 *minor,
++                  gss_name_t initiatorName,
++                  krb5_const_principal acceptorPrinc,
++                  krb5_keyblock *session,
++#ifdef HAVE_HEIMDAL_VERSION
++                  krb5_authdata *kdcIssuedAuthData
++#else
++                  krb5_authdata ***kdcIssuedAuthData
++#endif
++                  )
++{
++    OM_uint32 major, tmpMinor;
++    krb5_error_code code;
++    krb5_context krbContext;
++    gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER;
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_authdata authDataBuf, *authData = &authDataBuf;
++    AuthorizationDataElement authDatum = { 0 };
++#else
++    krb5_authdata *authData[2], authDatum = { 0 };
++#endif
++
++    memset(kdcIssuedAuthData, 0, sizeof(*kdcIssuedAuthData));
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    major = gssEapExportAttrContext(minor, initiatorName, &attrBuf);
++    if (GSS_ERROR(major))
++        return major;
++
++    authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP;
++#ifdef HAVE_HEIMDAL_VERSION
++    authDatum.ad_data.length = attrBuf.length;
++    authDatum.ad_data.data = attrBuf.value;
++    authData->len = 1;
++    authData->val = &authDatum;
++#else
++    authDatum.length = attrBuf.length;
++    authDatum.contents = attrBuf.value;
++    authData[0] = &authDatum;
++    authData[1] = NULL;
++#endif
++
++    code = krbMakeAuthDataKdcIssued(krbContext, session, acceptorPrinc,
++                                    authData, kdcIssuedAuthData);
++    if (code != 0) {
++        major = GSS_S_FAILURE;
++        *minor = code;
++    } else {
++        major = GSS_S_COMPLETE;
++    }
++
++    gss_release_buffer(&tmpMinor, &attrBuf);
++
++    return major;
++}
++
++/*
++ * Fabricate a ticket to ourselves given a GSS EAP context.
++ */
++OM_uint32
++gssEapMakeReauthCreds(OM_uint32 *minor,
++                      gss_ctx_id_t ctx,
++                      gss_cred_id_t cred,
++                      gss_buffer_t credBuf)
++{
++    OM_uint32 major = GSS_S_COMPLETE;
++    krb5_error_code code;
++    krb5_context krbContext = NULL;
++    krb5_keyblock session = { 0 }, acceptorKey = { 0 };
++    krb5_principal server = NULL;
++#ifdef HAVE_HEIMDAL_VERSION
++    Ticket ticket;
++    EncTicketPart enc_part;
++    AuthorizationData authData = { 0 };
++    krb5_crypto krbCrypto = NULL;
++    krb5_data ticketData = { 0 };
++    krb5_data encPartData = { 0 };
++    size_t len;
++#else
++    krb5_ticket ticket;
++    krb5_enc_tkt_part enc_part;
++    krb5_data *ticketData = NULL;
++#endif
++    krb5_data credsData = { 0 };
++    krb5_creds creds = { 0 };
++    krb5_auth_context authContext = NULL;
++
++    memset(&ticket, 0, sizeof(ticket));
++    memset(&enc_part, 0, sizeof(enc_part));
++
++    credBuf->length = 0;
++    credBuf->value = NULL;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    code = getAcceptorKey(krbContext, ctx, cred, &server, &acceptorKey);
++    if (code != 0) {
++        *minor = code;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    /*
++     * Generate a random session key to place in the ticket and
++     * sign the "KDC-Issued" authorization data element.
++     */
++#ifdef HAVE_HEIMDAL_VERSION
++    ticket.realm = server->realm;
++    ticket.sname = server->name;
++
++    code = krb5_generate_random_keyblock(krbContext, ctx->encryptionType,
++                                         &session);
++    if (code != 0)
++        goto cleanup;
++
++    enc_part.flags.initial = 1;
++    enc_part.key = session;
++    enc_part.crealm = ctx->initiatorName->krbPrincipal->realm;
++    enc_part.cname = ctx->initiatorName->krbPrincipal->name;
++    enc_part.authtime = time(NULL);
++    enc_part.starttime = &enc_part.authtime;
++    enc_part.endtime = (ctx->expiryTime != 0)
++                       ? ctx->expiryTime : KRB_TIME_FOREVER;
++    enc_part.renew_till = NULL;
++    enc_part.authorization_data = &authData;
++
++    major = freezeAttrContext(minor, ctx->initiatorName, server,
++                              &session, &authData);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    ASN1_MALLOC_ENCODE(EncTicketPart, encPartData.data, encPartData.length,
++                       &enc_part, &len, code);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_crypto_init(krbContext, &acceptorKey, 0, &krbCrypto);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_encrypt_EncryptedData(krbContext,
++                                      krbCrypto,
++                                      KRB5_KU_TICKET,
++                                      encPartData.data,
++                                      encPartData.length,
++                                      0,
++                                      &ticket.enc_part);
++    if (code != 0)
++        goto cleanup;
++
++    ASN1_MALLOC_ENCODE(Ticket, ticketData.data, ticketData.length,
++                       &ticket, &len, code);
++    if (code != 0)
++        goto cleanup;
++#else
++    ticket.server = server;
++
++    code = krb5_c_make_random_key(krbContext, ctx->encryptionType,
++                                  &session);
++    if (code != 0)
++        goto cleanup;
++
++    enc_part.flags = TKT_FLG_INITIAL;
++    enc_part.session = &session;
++    enc_part.client = ctx->initiatorName->krbPrincipal;
++    enc_part.times.authtime = time(NULL);
++    enc_part.times.starttime = enc_part.times.authtime;
++    enc_part.times.endtime = (ctx->expiryTime != 0)
++                             ? ctx->expiryTime
++                             : KRB_TIME_FOREVER;
++    enc_part.times.renew_till = 0;
++
++    major = freezeAttrContext(minor, ctx->initiatorName, server,
++                              &session, &enc_part.authorization_data);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    ticket.enc_part2 = &enc_part;
++
++    code = krb5_encrypt_tkt_part(krbContext, &acceptorKey, &ticket);
++    if (code != 0)
++        goto cleanup;
++
++    code = encode_krb5_ticket(&ticket, &ticketData);
++    if (code != 0)
++        goto cleanup;
++#endif /* HAVE_HEIMDAL_VERSION */
++
++    creds.client = ctx->initiatorName->krbPrincipal;
++    creds.server = server;
++#ifdef HAVE_HEIMDAL_VERSION
++    creds.session = session;
++    creds.times.authtime = enc_part.authtime;
++    creds.times.starttime = *enc_part.starttime;
++    creds.times.endtime = enc_part.endtime;
++    creds.times.renew_till = 0;
++    creds.flags.b = enc_part.flags;
++    creds.ticket = ticketData;
++    creds.authdata = authData;
++#else
++    creds.keyblock = session;
++    creds.times = enc_part.times;
++    creds.ticket_flags = enc_part.flags;
++    creds.ticket = *ticketData;
++    creds.authdata = enc_part.authorization_data;
++#endif
++
++    code = krb5_auth_con_init(krbContext, &authContext);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_auth_con_setflags(krbContext, authContext, 0);
++    if (code != 0)
++        goto cleanup;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_auth_con_setlocalsubkey(krbContext, authContext,
++                                        &ctx->rfc3961Key);
++#else
++    code = krb5_auth_con_setsendsubkey(krbContext, authContext,
++                                       &ctx->rfc3961Key);
++#endif
++    if (code != 0)
++        goto cleanup;
++
++    code = krbMakeCred(krbContext, authContext, &creds, &credsData);
++    if (code != 0)
++        goto cleanup;
++
++    krbDataToGssBuffer(&credsData, credBuf);
++
++cleanup:
++#ifdef HAVE_HEIMDAL_VERSION
++    if (krbCrypto != NULL)
++        krb5_crypto_destroy(krbContext, krbCrypto);
++    free_AuthorizationData(&authData);
++    free_EncryptedData(&ticket.enc_part);
++    krb5_data_free(&ticketData);
++    krb5_data_free(&encPartData);
++#else
++    krb5_free_authdata(krbContext, enc_part.authorization_data);
++    if (ticket.enc_part.ciphertext.data != NULL)
++        GSSEAP_FREE(ticket.enc_part.ciphertext.data);
++    krb5_free_data(krbContext, ticketData);
++#endif
++    krb5_free_keyblock_contents(krbContext, &session);
++    krb5_free_principal(krbContext, server);
++    krb5_free_keyblock_contents(krbContext, &acceptorKey);
++    krb5_auth_con_free(krbContext, authContext);
++
++    if (major == GSS_S_COMPLETE) {
++        *minor = code;
++        major = (code != 0) ? GSS_S_FAILURE : GSS_S_COMPLETE;
++    }
++
++    return major;
++}
++
++static int
++isTicketGrantingServiceP(krb5_context krbContext GSSEAP_UNUSED,
++                         krb5_const_principal principal)
++{
++    if (KRB_PRINC_LENGTH(principal) == 2 &&
++#ifdef HAVE_HEIMDAL_VERSION
++        strcmp(KRB_PRINC_NAME(principal)[0], "krbtgt") == 0
++#else
++        krb5_princ_component(krbContext, principal, 0)->length == 6 &&
++        memcmp(krb5_princ_component(krbContext,
++                                    principal, 0)->data, "krbtgt", 6) == 0
++#endif
++        )
++        return TRUE;
++
++    return FALSE;
++}
++
++/*
++ * Returns TRUE if the configuration variable reauth_use_ccache is
++ * set in krb5.conf for the eap_gss application and the client realm.
++ */
++static int
++reauthUseCredsCache(krb5_context krbContext,
++                    krb5_principal principal)
++{
++    int reauthUseCCache;
++
++    /* if reauth_use_ccache, use default credentials cache if ticket is for us */
++    krb5_appdefault_boolean(krbContext, "eap_gss",
++                            KRB_PRINC_REALM(principal),
++                            "reauth_use_ccache", 0, &reauthUseCCache);
++
++    return reauthUseCCache;
++}
++
++/*
++ * Look in default credentials cache for reauthentication credentials,
++ * if policy allows.
++ */
++static OM_uint32
++getDefaultReauthCredentials(OM_uint32 *minor,
++                            gss_cred_id_t cred,
++                            gss_name_t target,
++                            time_t now,
++                            OM_uint32 timeReq)
++{
++    OM_uint32 major = GSS_S_CRED_UNAVAIL;
++    krb5_context krbContext = NULL;
++    krb5_error_code code = 0;
++    krb5_ccache ccache = NULL;
++    krb5_creds match = { 0 };
++    krb5_creds creds = { 0 };
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
++    GSSEAP_ASSERT(target != GSS_C_NO_NAME);
++
++    if (cred->name == GSS_C_NO_NAME ||
++        !reauthUseCredsCache(krbContext, cred->name->krbPrincipal))
++        goto cleanup;
++
++    match.client = cred->name->krbPrincipal;
++    match.server = target->krbPrincipal;
++    if (timeReq != 0 && timeReq != GSS_C_INDEFINITE)
++        match.times.endtime = now + timeReq;
++
++    code = krb5_cc_default(krbContext, &ccache);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_cc_retrieve_cred(krbContext, ccache, 0, &match, &creds);
++    if (code != 0)
++        goto cleanup;
++
++    cred->flags |= CRED_FLAG_DEFAULT_CCACHE;
++    cred->krbCredCache = ccache;
++    ccache = NULL;
++
++    major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL,
++                                 &cred->reauthCred);
++
++cleanup:
++    if (major == GSS_S_CRED_UNAVAIL)
++        *minor = code;
++
++    if (ccache != NULL)
++        krb5_cc_close(krbContext, ccache);
++    krb5_free_cred_contents(krbContext, &creds);
++
++    return major;
++}
++
++/*
++ * Returns TRUE if the credential handle's reauth credentials are
++ * valid or if we can use the default credentials cache. Credentials
++ * handle must be locked.
++ */
++int
++gssEapCanReauthP(gss_cred_id_t cred,
++                 gss_name_t target,
++                 OM_uint32 timeReq)
++{
++    time_t now, expiryReq;
++    OM_uint32 minor;
++
++    if (cred == GSS_C_NO_CREDENTIAL)
++        return FALSE;
++
++    now = time(NULL);
++    expiryReq = now;
++    if (timeReq != GSS_C_INDEFINITE)
++        expiryReq += timeReq;
++
++    if (cred->krbCredCache != NULL && cred->expiryTime > expiryReq)
++        return TRUE;
++
++    if (getDefaultReauthCredentials(&minor, cred, target,
++                                    now, timeReq) == GSS_S_COMPLETE)
++        return TRUE;
++
++    return FALSE;
++}
++
++/*
++ * Store re-authentication (Kerberos) credentials in a credential handle.
++ * Credentials handle must be locked.
++ */
++OM_uint32
++gssEapStoreReauthCreds(OM_uint32 *minor,
++                       gss_ctx_id_t ctx,
++                       gss_cred_id_t cred,
++                       gss_buffer_t credBuf)
++{
++    OM_uint32 major = GSS_S_COMPLETE;
++    krb5_error_code code;
++    krb5_context krbContext = NULL;
++    krb5_auth_context authContext = NULL;
++    krb5_data credData = { 0 };
++    krb5_creds **creds = NULL;
++    krb5_principal canonPrinc;
++    krb5_principal ccPrinc = NULL;
++    int i;
++
++    if (credBuf->length == 0 || cred == GSS_C_NO_CREDENTIAL)
++        return GSS_S_COMPLETE;
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    code = krb5_auth_con_init(krbContext, &authContext);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_auth_con_setflags(krbContext, authContext, 0);
++    if (code != 0)
++        goto cleanup;
++
++    code = krb5_auth_con_setrecvsubkey(krbContext, authContext,
++                                       &ctx->rfc3961Key);
++    if (code != 0)
++        goto cleanup;
++
++    gssBufferToKrbData(credBuf, &credData);
++
++    code = krb5_rd_cred(krbContext, authContext, &credData, &creds, NULL);
++    if (code != 0)
++        goto cleanup;
++
++    if (creds == NULL || creds[0] == NULL)
++        goto cleanup;
++
++    code = krb5_copy_principal(krbContext, creds[0]->client, &canonPrinc);
++    if (code != 0)
++        goto cleanup;
++
++    krb5_free_principal(krbContext, cred->name->krbPrincipal);
++    cred->name->krbPrincipal = canonPrinc;
++
++    if (creds[0]->times.endtime == KRB_TIME_FOREVER)
++        cred->expiryTime = 0;
++    else
++        cred->expiryTime = creds[0]->times.endtime;
++
++    if (cred->krbCredCache == NULL) {
++        if (reauthUseCredsCache(krbContext, creds[0]->client) &&
++            krb5_cc_default(krbContext, &cred->krbCredCache) == 0)
++            cred->flags |= CRED_FLAG_DEFAULT_CCACHE;
++    } else {
++        /*
++         * If we already have an associated credentials cache, possibly from
++         * the last time we stored a reauthentication credential, then we
++         * need to clear it out and release the associated GSS credential.
++         */
++        if (cred->flags & CRED_FLAG_DEFAULT_CCACHE) {
++            krb5_cc_remove_cred(krbContext, cred->krbCredCache, 0, creds[0]);
++        } else {
++            krb5_cc_destroy(krbContext, cred->krbCredCache);
++            cred->krbCredCache = NULL;
++        }
++        gssReleaseCred(minor, &cred->reauthCred);
++    }
++
++    if (cred->krbCredCache == NULL) {
++        code = krb5_cc_new_unique(krbContext, "MEMORY", NULL, &cred->krbCredCache);
++        if (code != 0)
++            goto cleanup;
++    }
++
++    if ((cred->flags & CRED_FLAG_DEFAULT_CCACHE) == 0 ||
++        krb5_cc_get_principal(krbContext, cred->krbCredCache, &ccPrinc) != 0) {
++        code = krb5_cc_initialize(krbContext, cred->krbCredCache,
++                                  creds[0]->client);
++        if (code != 0)
++            goto cleanup;
++    }
++
++    for (i = 0; creds[i] != NULL; i++) {
++        krb5_creds kcred = *(creds[i]);
++
++        /*
++         * Swap in the acceptor name the client asked for so
++         * get_credentials() works. We're making the assumption that
++         * any service tickets returned are for us. We'll need to
++         * reflect some more on whether that is a safe assumption.
++         */
++        if (!isTicketGrantingServiceP(krbContext, kcred.server))
++            kcred.server = ctx->acceptorName->krbPrincipal;
++
++        code = krb5_cc_store_cred(krbContext, cred->krbCredCache, &kcred);
++        if (code != 0)
++            goto cleanup;
++    }
++
++    major = gss_krb5_import_cred(minor, cred->krbCredCache, NULL, NULL,
++                                 &cred->reauthCred);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    *minor = code;
++
++    krb5_free_principal(krbContext, ccPrinc);
++    krb5_auth_con_free(krbContext, authContext);
++    if (creds != NULL) {
++        for (i = 0; creds[i] != NULL; i++)
++            krb5_free_creds(krbContext, creds[i]);
++        GSSEAP_FREE(creds);
++    }
++    if (major == GSS_S_COMPLETE)
++        major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE;
++
++    return major;
++}
++
++#ifndef HAVE_HEIMDAL_VERSION
++static gss_buffer_desc radiusAvpKrbAttr = {
++    sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp"
++};
++#endif
++
++/*
++ * Unfortunately extracting an AD-KDCIssued authorization data element
++ * is pretty implementation-dependent. It's not possible to verify the
++ * signature ourselves because the ticket session key is not exposed
++ * outside GSS. In an ideal world, all AD-KDCIssued elements would be
++ * verified by the Kerberos library and authentication would fail if
++ * verification failed. We're not quite there yet and as a result have
++ * to go through some hoops to get this to work. The alternative would
++ * be to sign the authorization data with our long-term key, but it
++ * seems a pity to compromise the design because of current implementation
++ * limitations.
++ *
++ * (Specifically, the hoops involve a libkrb5 authorisation data plugin
++ * that exposes the verified and serialised attribute context through
++ * the Kerberos GSS mechanism's naming extensions API.)
++ */
++static OM_uint32
++defrostAttrContext(OM_uint32 *minor,
++#ifdef HAVE_HEIMDAL_VERSION
++                   gss_ctx_id_t glueContext,
++#else
++                   gss_name_t glueName,
++#endif
++                   gss_name_t mechName)
++{
++    OM_uint32 major, tmpMinor;
++#ifdef HAVE_HEIMDAL_VERSION
++    gss_OID_desc oid = { 0 };
++    gss_buffer_set_t authData = GSS_C_NO_BUFFER_SET;
++#else
++    gss_buffer_desc authData = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc authDataDisplay = GSS_C_EMPTY_BUFFER;
++    int more = -1;
++    int authenticated, complete;
++#endif
++
++#ifdef HAVE_HEIMDAL_VERSION
++    major = composeOid(minor,
++                       GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
++                       GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
++                       KRB5_AUTHDATA_RADIUS_AVP, &oid);
++    if (GSS_ERROR(major))
++        return major;
++
++    /* XXX we are assuming that this verifies AD-KDCIssued signature */
++    major = gssInquireSecContextByOid(minor, glueContext,
++                                      &oid, &authData);
++    if (major == GSS_S_COMPLETE) {
++        if (authData == GSS_C_NO_BUFFER_SET || authData->count != 1)
++            major = GSS_S_FAILURE;
++        else
++            major = gssEapImportAttrContext(minor, authData->elements, mechName);
++    } else if (major == GSS_S_FAILURE && *minor == ENOENT) {
++        /* This is the equivalent of GSS_S_UNAVAILABLE for MIT attr APIs */
++        *minor = 0;
++        major = GSS_S_COMPLETE;
++    }
++
++    gss_release_buffer_set(&tmpMinor, &authData);
++    GSSEAP_FREE(oid.elements);
++#else
++    major = gssGetNameAttribute(minor, glueName, &radiusAvpKrbAttr,
++                                &authenticated, &complete,
++                                &authData, &authDataDisplay, &more);
++    if (major == GSS_S_COMPLETE) {
++        if (authenticated == 0)
++            major = GSS_S_BAD_NAME;
++        else
++            major = gssEapImportAttrContext(minor, &authData, mechName);
++    } else if (major == GSS_S_UNAVAILABLE) {
++        major = GSS_S_COMPLETE;
++    }
++
++    gss_release_buffer(&tmpMinor, &authData);
++    gss_release_buffer(&tmpMinor, &authDataDisplay);
++#endif /* HAVE_HEIMDAL_VERSION */
++
++    return major;
++}
++
++/*
++ * Convert a mechanism glue to an EAP mechanism name by displaying and
++ * importing it. This also handles the RADIUS attributes.
++ */
++OM_uint32
++gssEapGlueToMechName(OM_uint32 *minor,
++                     gss_ctx_id_t ctx,
++                     gss_name_t glueName,
++                     gss_name_t *pMechName)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
++
++    *pMechName = GSS_C_NO_NAME;
++
++    major = gssDisplayName(minor, glueName, &nameBuf, NULL);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
++                             ctx->mechanismUsed, pMechName);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = defrostAttrContext(minor,
++#ifdef HAVE_HEIMDAL_VERSION
++                               ctx->reauthCtx,
++#else
++                               glueName,
++#endif
++                               *pMechName);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        gssReleaseName(&tmpMinor, pMechName);
++        *pMechName = GSS_C_NO_NAME;
++    }
++
++    gss_release_buffer(&tmpMinor, &nameBuf);
++
++    return major;
++}
++
++/*
++ * Convert an EAP mechanism name to a mechanism glue name by displaying
++ * and importing it.
++ */
++OM_uint32
++gssEapMechToGlueName(OM_uint32 *minor,
++                     gss_name_t mechName,
++                     gss_name_t *pGlueName)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
++
++    *pGlueName = GSS_C_NO_NAME;
++
++    major = gssEapDisplayName(minor, mechName, &nameBuf, NULL);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
++                          pGlueName);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    gss_release_buffer(&tmpMinor, &nameBuf);
++
++    return major;
++}
++
++/*
++ * Suck out the analgous elements of a Kerberos GSS context into an EAP
++ * one so that the application doesn't know the difference.
++ */
++OM_uint32
++gssEapReauthComplete(OM_uint32 *minor,
++                     gss_ctx_id_t ctx,
++                     gss_cred_id_t cred GSSEAP_UNUSED,
++                     const gss_OID mech,
++                     OM_uint32 timeRec)
++{
++    OM_uint32 major, tmpMinor;
++    gss_buffer_set_t keyData = GSS_C_NO_BUFFER_SET;
++    krb5_context krbContext = NULL;
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_storage *sp = NULL;
++#endif
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    if (!oidEqual(mech, gss_mech_krb5)) {
++        major = GSS_S_BAD_MECH;
++        goto cleanup;
++    }
++
++    /* Get the raw subsession key and encryption type */
++#ifdef HAVE_HEIMDAL_VERSION
++#define KRB_GSS_SUBKEY_COUNT    1 /* encoded session key */
++    major = gssInquireSecContextByOid(minor, ctx->reauthCtx,
++                                      GSS_KRB5_GET_SUBKEY_X, &keyData);
++#else
++#define KRB_GSS_SUBKEY_COUNT    2 /* raw session key, enctype OID */
++    major = gssInquireSecContextByOid(minor, ctx->reauthCtx,
++                                      GSS_C_INQ_SSPI_SESSION_KEY, &keyData);
++#endif
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (keyData == GSS_C_NO_BUFFER_SET || keyData->count < KRB_GSS_SUBKEY_COUNT) {
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        major = GSS_S_FAILURE;
++        goto cleanup;
++    }
++
++#ifdef HAVE_HEIMDAL_VERSION
++    sp = krb5_storage_from_mem(keyData->elements[0].value,
++                               keyData->elements[0].length);
++    if (sp == NULL) {
++        *minor = ENOMEM;
++        major = GSS_S_FAILURE;
++        goto cleanup;
++    }
++
++    *minor = krb5_ret_keyblock(sp, &ctx->rfc3961Key);
++    if (*minor != 0) {
++        major = GSS_S_FAILURE;
++        goto cleanup;
++    }
++#else
++    {
++        gss_OID_desc oid;
++        int suffix;
++
++        oid.length = keyData->elements[1].length;
++        oid.elements = keyData->elements[1].value;
++
++        /* GSS_KRB5_SESSION_KEY_ENCTYPE_OID */
++        major = decomposeOid(minor,
++                             "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04",
++                             10, &oid, &suffix);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        ctx->encryptionType = suffix;
++    }
++
++    {
++        krb5_keyblock key;
++
++        KRB_KEY_LENGTH(&key) = keyData->elements[0].length;
++        KRB_KEY_DATA(&key)   = keyData->elements[0].value;
++        KRB_KEY_TYPE(&key)   = ctx->encryptionType;
++
++        *minor = krb5_copy_keyblock_contents(krbContext,
++                                             &key, &ctx->rfc3961Key);
++        if (*minor != 0) {
++            major = GSS_S_FAILURE;
++            goto cleanup;
++        }
++    }
++#endif /* HAVE_HEIMDAL_VERSION */
++
++    major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
++                                      &ctx->checksumType);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    if (timeRec != GSS_C_INDEFINITE)
++        ctx->expiryTime = time(NULL) + timeRec;
++
++    /* Initialize our sequence state */
++    major = sequenceInit(minor,
++                         &ctx->seqState, ctx->recvSeq,
++                         ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
++                         ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
++                         TRUE);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = GSS_S_COMPLETE;
++
++cleanup:
++#ifdef HAVE_HEIMDAL_VERSION
++    if (sp != NULL)
++        krb5_storage_free(sp);
++#endif
++    gss_release_buffer_set(&tmpMinor, &keyData);
++
++    return major;
++}
++
++/*
++ * The remainder of this file consists of wrappers so we can call into the
++ * mechanism glue without calling ourselves.
++ */
++static OM_uint32
++(*gssInitSecContextNext)(OM_uint32 *,
++                         gss_cred_id_t,
++                         gss_ctx_id_t *,
++                         gss_name_t,
++                         gss_OID,
++                         OM_uint32,
++                         OM_uint32,
++                         gss_channel_bindings_t,
++                         gss_buffer_t,
++                         gss_OID *,
++                         gss_buffer_t,
++                         OM_uint32 *,
++                         OM_uint32 *);
++
++static OM_uint32
++(*gssAcceptSecContextNext)(OM_uint32 *,
++                           gss_ctx_id_t *,
++                           gss_cred_id_t,
++                           gss_buffer_t,
++                           gss_channel_bindings_t,
++                           gss_name_t *,
++                           gss_OID *,
++                           gss_buffer_t,
++                           OM_uint32 *,
++                           OM_uint32 *,
++                           gss_cred_id_t *);
++
++static OM_uint32
++(*gssReleaseCredNext)(OM_uint32 *, gss_cred_id_t *);
++
++static OM_uint32
++(*gssReleaseNameNext)(OM_uint32 *, gss_name_t *);
++
++static OM_uint32
++(*gssInquireSecContextByOidNext)(OM_uint32 *,
++                                 const gss_ctx_id_t,
++                                 const gss_OID,
++                                 gss_buffer_set_t *);
++
++static OM_uint32
++(*gssDeleteSecContextNext)(OM_uint32 *,
++                          gss_ctx_id_t *,
++                          gss_buffer_t);
++
++static OM_uint32
++(*gssDisplayNameNext)(OM_uint32 *,
++                      gss_name_t,
++                      gss_buffer_t,
++                      gss_OID *);
++
++static OM_uint32
++(*gssImportNameNext)(OM_uint32 *,
++                     gss_buffer_t,
++                     gss_OID,
++                     gss_name_t *);
++
++static OM_uint32
++(*gssStoreCredNext)(OM_uint32 *,
++                    const gss_cred_id_t,
++                    gss_cred_usage_t,
++                    const gss_OID,
++                    OM_uint32,
++                    OM_uint32,
++                    gss_OID_set *,
++                    gss_cred_usage_t *);
++
++static OM_uint32
++(*gssGetNameAttributeNext)(OM_uint32 *,
++                          gss_name_t,
++                          gss_buffer_t,
++                          int *,
++                          int *,
++                          gss_buffer_t,
++                          gss_buffer_t,
++                          int *);
++
++#define NEXT_SYMBOL(local, global)  do {        \
++        ((local) = dlsym(RTLD_NEXT, (global))); \
++        if ((local) == NULL) {                  \
++            *minor = GSSEAP_NO_MECHGLUE_SYMBOL; \
++            major = GSS_S_UNAVAILABLE;          \
++            /* but continue */                  \
++        }                                       \
++    } while (0)
++
++OM_uint32
++gssEapReauthInitialize(OM_uint32 *minor)
++{
++    OM_uint32 major = GSS_S_COMPLETE;
++
++    NEXT_SYMBOL(gssInitSecContextNext,         "gss_init_sec_context");
++    NEXT_SYMBOL(gssAcceptSecContextNext,       "gss_accept_sec_context");
++    NEXT_SYMBOL(gssReleaseCredNext,            "gss_release_cred");
++    NEXT_SYMBOL(gssReleaseNameNext,            "gss_release_name");
++    NEXT_SYMBOL(gssInquireSecContextByOidNext, "gss_inquire_sec_context_by_oid");
++    NEXT_SYMBOL(gssDeleteSecContextNext,       "gss_delete_sec_context");
++    NEXT_SYMBOL(gssDisplayNameNext,            "gss_display_name");
++    NEXT_SYMBOL(gssImportNameNext,             "gss_import_name");
++    NEXT_SYMBOL(gssStoreCredNext,              "gss_store_cred");
++#ifndef HAVE_HEIMDAL_VERSION
++    NEXT_SYMBOL(gssGetNameAttributeNext,       "gss_get_name_attribute");
++#endif
++
++    return major;
++}
++
++OM_uint32
++gssInitSecContext(OM_uint32 *minor,
++                  gss_cred_id_t cred,
++                  gss_ctx_id_t *context_handle,
++                  gss_name_t target_name,
++                  gss_OID mech_type,
++                  OM_uint32 req_flags,
++                  OM_uint32 time_req,
++                  gss_channel_bindings_t input_chan_bindings,
++                  gss_buffer_t input_token,
++                  gss_OID *actual_mech_type,
++                  gss_buffer_t output_token,
++                  OM_uint32 *ret_flags,
++                  OM_uint32 *time_rec)
++{
++    if (gssInitSecContextNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssInitSecContextNext(minor, cred, context_handle,
++                                 target_name, mech_type, req_flags,
++                                 time_req, input_chan_bindings,
++                                 input_token, actual_mech_type,
++                                 output_token, ret_flags, time_rec);
++}
++
++OM_uint32
++gssAcceptSecContext(OM_uint32 *minor,
++                    gss_ctx_id_t *context_handle,
++                    gss_cred_id_t cred,
++                    gss_buffer_t input_token,
++                    gss_channel_bindings_t input_chan_bindings,
++                    gss_name_t *src_name,
++                    gss_OID *mech_type,
++                    gss_buffer_t output_token,
++                    OM_uint32 *ret_flags,
++                    OM_uint32 *time_rec,
++                    gss_cred_id_t *delegated_cred_handle)
++{
++    if (gssAcceptSecContextNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssAcceptSecContextNext(minor, context_handle, cred,
++                                   input_token, input_chan_bindings,
++                                   src_name, mech_type, output_token,
++                                   ret_flags, time_rec, delegated_cred_handle);
++}
++
++OM_uint32
++gssReleaseCred(OM_uint32 *minor,
++               gss_cred_id_t *cred_handle)
++{
++    if (gssReleaseCredNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssReleaseCredNext(minor, cred_handle);
++}
++
++OM_uint32
++gssReleaseName(OM_uint32 *minor,
++               gss_name_t *name)
++{
++    if (gssReleaseName == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssReleaseNameNext(minor, name);
++}
++
++OM_uint32
++gssDeleteSecContext(OM_uint32 *minor,
++                    gss_ctx_id_t *context_handle,
++                    gss_buffer_t output_token)
++{
++    if (gssDeleteSecContextNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssDeleteSecContextNext(minor, context_handle, output_token);
++}
++
++static OM_uint32
++gssDisplayName(OM_uint32 *minor,
++               gss_name_t name,
++               gss_buffer_t buffer,
++               gss_OID *name_type)
++{
++    if (gssDisplayNameNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssDisplayNameNext(minor, name, buffer, name_type);
++}
++
++static OM_uint32
++gssImportName(OM_uint32 *minor,
++              gss_buffer_t buffer,
++              gss_OID name_type,
++              gss_name_t *name)
++{
++    if (gssImportNameNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssImportNameNext(minor, buffer, name_type, name);
++}
++
++OM_uint32
++gssInquireSecContextByOid(OM_uint32 *minor,
++                          const gss_ctx_id_t context_handle,
++                          const gss_OID desired_object,
++                          gss_buffer_set_t *data_set)
++{
++    if (gssInquireSecContextByOidNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssInquireSecContextByOidNext(minor, context_handle,
++                                         desired_object, data_set);
++}
++
++OM_uint32
++gssStoreCred(OM_uint32 *minor,
++             const gss_cred_id_t input_cred_handle,
++             gss_cred_usage_t input_usage,
++             const gss_OID desired_mech,
++             OM_uint32 overwrite_cred,
++             OM_uint32 default_cred,
++             gss_OID_set *elements_stored,
++             gss_cred_usage_t *cred_usage_stored)
++{
++    if (gssStoreCredNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssStoreCredNext(minor, input_cred_handle, input_usage,
++                            desired_mech, overwrite_cred, default_cred,
++                            elements_stored, cred_usage_stored);
++}
++
++OM_uint32
++gssGetNameAttribute(OM_uint32 *minor,
++                    gss_name_t name,
++                    gss_buffer_t attr,
++                    int *authenticated,
++                    int *complete,
++                    gss_buffer_t value,
++                    gss_buffer_t display_value,
++                    int *more)
++{
++    if (gssGetNameAttributeNext == NULL) {
++        *minor = GSSEAP_NO_MECHGLUE_SYMBOL;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    return gssGetNameAttributeNext(minor, name, attr, authenticated, complete,
++                                   value, display_value, more);
++}
+diff --git a/mech_eap/util_reauth.h b/mech_eap/util_reauth.h
+new file mode 100644
+index 0000000..9b9f264
+--- /dev/null
++++ b/mech_eap/util_reauth.h
+@@ -0,0 +1,151 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Fast reauthentication support.
++ */
++
++#include "gssapiP_eap.h"
++
++#ifndef _UTIL_REAUTH_H_
++#define _UTIL_REAUTH_H_ 1
++
++/* AD element containing serialised AVPs. */
++#define KRB5_AUTHDATA_RADIUS_AVP        513
++
++OM_uint32
++gssInitSecContext(OM_uint32 *minor,
++                  gss_cred_id_t cred,
++                  gss_ctx_id_t *context_handle,
++                  gss_name_t target_name,
++                  gss_OID mech_type,
++                  OM_uint32 req_flags,
++                  OM_uint32 time_req,
++                  gss_channel_bindings_t input_chan_bindings,
++                  gss_buffer_t input_token,
++                  gss_OID *actual_mech_type,
++                  gss_buffer_t output_token,
++                  OM_uint32 *ret_flags,
++                  OM_uint32 *time_rec);
++
++OM_uint32
++gssAcceptSecContext(OM_uint32 *minor,
++                    gss_ctx_id_t *context_handle,
++                    gss_cred_id_t cred,
++                    gss_buffer_t input_token,
++                    gss_channel_bindings_t input_chan_bindings,
++                    gss_name_t *src_name,
++                    gss_OID *mech_type,
++                    gss_buffer_t output_token,
++                    OM_uint32 *ret_flags,
++                    OM_uint32 *time_rec,
++                    gss_cred_id_t *delegated_cred_handle);
++
++OM_uint32
++gssReleaseCred(OM_uint32 *minor,
++               gss_cred_id_t *cred_handle);
++
++OM_uint32
++gssReleaseName(OM_uint32 *minor,
++               gss_name_t *name);
++
++OM_uint32
++gssDeleteSecContext(OM_uint32 *minor,
++                    gss_ctx_id_t *context_handle,
++                    gss_buffer_t output_token);
++
++OM_uint32
++gssInquireSecContextByOid(OM_uint32 *minor,
++                          const gss_ctx_id_t context_handle,
++                          const gss_OID desired_object,
++                          gss_buffer_set_t *data_set);
++
++OM_uint32
++gssStoreCred(OM_uint32 *minor,
++             const gss_cred_id_t input_cred_handle,
++             gss_cred_usage_t input_usage,
++             const gss_OID desired_mech,
++             OM_uint32 overwrite_cred,
++             OM_uint32 default_cred,
++             gss_OID_set *elements_stored,
++             gss_cred_usage_t *cred_usage_stored);
++
++OM_uint32
++gssGetNameAttribute(OM_uint32 *minor,
++                    gss_name_t name,
++                    gss_buffer_t attr,
++                    int *authenticated,
++                    int *complete,
++                    gss_buffer_t value,
++                    gss_buffer_t display_value,
++                    int *more);
++
++OM_uint32
++gssEapMakeReauthCreds(OM_uint32 *minor,
++                      gss_ctx_id_t ctx,
++                      gss_cred_id_t cred,
++                      gss_buffer_t credBuf);
++
++OM_uint32
++gssEapStoreReauthCreds(OM_uint32 *minor,
++                       gss_ctx_id_t ctx,
++                       gss_cred_id_t cred,
++                       gss_buffer_t credBuf);
++
++
++OM_uint32
++gssEapGlueToMechName(OM_uint32 *minor,
++                     gss_ctx_id_t glueContext,
++                     gss_name_t glueName,
++                     gss_name_t *pMechName);
++
++OM_uint32
++gssEapMechToGlueName(OM_uint32 *minor,
++                     gss_name_t mechName,
++                     gss_name_t *pGlueName);
++
++OM_uint32
++gssEapReauthComplete(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    gss_cred_id_t cred,
++                    const gss_OID mech,
++                    OM_uint32 timeRec);
++
++OM_uint32
++gssEapReauthInitialize(OM_uint32 *minor);
++
++int
++gssEapCanReauthP(gss_cred_id_t cred,
++                 gss_name_t target,
++                 OM_uint32 timeReq);
++
++#endif /* _UTIL_REAUTH_H_ */
+diff --git a/mech_eap/util_saml.cpp b/mech_eap/util_saml.cpp
+new file mode 100644
+index 0000000..ce7582e
+--- /dev/null
++++ b/mech_eap/util_saml.cpp
+@@ -0,0 +1,775 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * SAML attribute provider implementation.
++ */
++
++#include "gssapiP_eap.h"
++
++#include <sstream>
++
++#include <xercesc/util/XMLUniDefs.hpp>
++#include <xmltooling/unicode.h>
++#include <xmltooling/XMLToolingConfig.h>
++#include <xmltooling/util/XMLHelper.h>
++#include <xmltooling/util/ParserPool.h>
++#include <xmltooling/util/DateTime.h>
++
++#include <saml/exceptions.h>
++#include <saml/SAMLConfig.h>
++#include <saml/saml1/core/Assertions.h>
++#include <saml/saml2/core/Assertions.h>
++#include <saml/saml2/metadata/Metadata.h>
++#include <saml/saml2/metadata/MetadataProvider.h>
++
++using namespace xmltooling;
++using namespace opensaml::saml2md;
++using namespace opensaml;
++using namespace xercesc;
++using namespace std;
++
++static const XMLCh
++base64Binary[] = {'b','a','s','e','6','4','B','i','n','a','r','y',0};
++
++/*
++ * gss_eap_saml_assertion_provider is for retrieving the underlying
++ * assertion.
++ */
++gss_eap_saml_assertion_provider::gss_eap_saml_assertion_provider(void)
++{
++    m_assertion = NULL;
++    m_authenticated = false;
++}
++
++gss_eap_saml_assertion_provider::~gss_eap_saml_assertion_provider(void)
++{
++    delete m_assertion;
++}
++
++bool
++gss_eap_saml_assertion_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
++                                                         const gss_eap_attr_provider *ctx)
++{
++    /* Then we may be creating from an existing attribute context */
++    const gss_eap_saml_assertion_provider *saml;
++
++    GSSEAP_ASSERT(m_assertion == NULL);
++
++    if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx))
++        return false;
++
++    saml = static_cast<const gss_eap_saml_assertion_provider *>(ctx);
++    setAssertion(saml->getAssertion(), saml->authenticated());
++
++    return true;
++}
++
++bool
++gss_eap_saml_assertion_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
++                                                    const gss_cred_id_t gssCred,
++                                                    const gss_ctx_id_t gssCtx)
++{
++    const gss_eap_radius_attr_provider *radius;
++    gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
++    int authenticated, complete;
++    OM_uint32 minor;
++
++    GSSEAP_ASSERT(m_assertion == NULL);
++
++    if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
++        return false;
++
++    /*
++     * XXX TODO we need to support draft-howlett-radius-saml-attr-00
++     */
++    radius = static_cast<const gss_eap_radius_attr_provider *>
++        (m_manager->getProvider(ATTR_TYPE_RADIUS));
++    if (radius != NULL &&
++        radius->getFragmentedAttribute(PW_SAML_AAA_ASSERTION,
++                                       VENDORPEC_UKERNA,
++                                       &authenticated, &complete, &value)) {
++        setAssertion(&value, authenticated);
++        gss_release_buffer(&minor, &value);
++    } else {
++        m_assertion = NULL;
++    }
++
++    return true;
++}
++
++void
++gss_eap_saml_assertion_provider::setAssertion(const saml2::Assertion *assertion,
++                                              bool authenticated)
++{
++
++    delete m_assertion;
++
++    if (assertion != NULL) {
++#ifdef __APPLE__
++        m_assertion = (saml2::Assertion *)((void *)assertion->clone());
++#else
++        m_assertion = dynamic_cast<saml2::Assertion *>(assertion->clone());
++#endif
++        m_authenticated = authenticated;
++    } else {
++        m_assertion = NULL;
++        m_authenticated = false;
++    }
++}
++
++void
++gss_eap_saml_assertion_provider::setAssertion(const gss_buffer_t buffer,
++                                              bool authenticated)
++{
++    delete m_assertion;
++
++    m_assertion = parseAssertion(buffer);
++    m_authenticated = (m_assertion != NULL && authenticated);
++}
++
++saml2::Assertion *
++gss_eap_saml_assertion_provider::parseAssertion(const gss_buffer_t buffer)
++{
++    string str((char *)buffer->value, buffer->length);
++    istringstream istream(str);
++    DOMDocument *doc;
++    const XMLObjectBuilder *b;
++
++    try {
++        doc = XMLToolingConfig::getConfig().getParser().parse(istream);
++        if (doc == NULL)
++            return NULL;
++
++        b = XMLObjectBuilder::getBuilder(doc->getDocumentElement());
++
++#ifdef __APPLE__
++        return (saml2::Assertion *)((void *)b->buildFromDocument(doc));
++#else
++        return dynamic_cast<saml2::Assertion *>(b->buildFromDocument(doc));
++#endif
++    } catch (exception &e) {
++        return NULL;
++    }
++}
++
++bool
++gss_eap_saml_assertion_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
++                                                   void *data) const
++{
++    bool ret;
++
++    /* just add the prefix */
++    if (m_assertion != NULL)
++        ret = addAttribute(m_manager, this, GSS_C_NO_BUFFER, data);
++    else
++        ret = true;
++
++    return ret;
++}
++
++bool
++gss_eap_saml_assertion_provider::setAttribute(int complete GSSEAP_UNUSED,
++                                              const gss_buffer_t attr,
++                                              const gss_buffer_t value)
++{
++    if (attr == GSS_C_NO_BUFFER || attr->length == 0) {
++        setAssertion(value);
++        return true;
++    }
++
++    return false;
++}
++
++bool
++gss_eap_saml_assertion_provider::deleteAttribute(const gss_buffer_t value GSSEAP_UNUSED)
++{
++    delete m_assertion;
++    m_assertion = NULL;
++    m_authenticated = false;
++
++    return true;
++}
++
++time_t
++gss_eap_saml_assertion_provider::getExpiryTime(void) const
++{
++    saml2::Conditions *conditions;
++    time_t expiryTime = 0;
++
++    if (m_assertion == NULL)
++        return 0;
++
++    conditions = m_assertion->getConditions();
++
++    if (conditions != NULL && conditions->getNotOnOrAfter() != NULL)
++        expiryTime = conditions->getNotOnOrAfter()->getEpoch();
++
++    return expiryTime;
++}
++
++OM_uint32
++gss_eap_saml_assertion_provider::mapException(OM_uint32 *minor,
++                                              std::exception &e) const
++{
++    if (typeid(e) == typeid(SecurityPolicyException))
++        *minor = GSSEAP_SAML_SEC_POLICY_FAILURE;
++    else if (typeid(e) == typeid(BindingException))
++        *minor = GSSEAP_SAML_BINDING_FAILURE;
++    else if (typeid(e) == typeid(ProfileException))
++        *minor = GSSEAP_SAML_PROFILE_FAILURE;
++    else if (typeid(e) == typeid(FatalProfileException))
++        *minor = GSSEAP_SAML_FATAL_PROFILE_FAILURE;
++    else if (typeid(e) == typeid(RetryableProfileException))
++        *minor = GSSEAP_SAML_RETRY_PROFILE_FAILURE;
++    else if (typeid(e) == typeid(MetadataException))
++        *minor = GSSEAP_SAML_METADATA_FAILURE;
++    else
++        return GSS_S_CONTINUE_NEEDED;
++
++    gssEapSaveStatusInfo(*minor, "%s", e.what());
++
++    return GSS_S_FAILURE;
++}
++
++bool
++gss_eap_saml_assertion_provider::getAttribute(const gss_buffer_t attr,
++                                              int *authenticated,
++                                              int *complete,
++                                              gss_buffer_t value,
++                                              gss_buffer_t display_value GSSEAP_UNUSED,
++                                              int *more) const
++{
++    string str;
++
++    if (attr != GSS_C_NO_BUFFER && attr->length != 0)
++        return false;
++
++    if (m_assertion == NULL)
++        return false;
++
++    if (*more != -1)
++        return false;
++
++    if (authenticated != NULL)
++        *authenticated = m_authenticated;
++    if (complete != NULL)
++        *complete = true;
++
++    XMLHelper::serialize(m_assertion->marshall((DOMDocument *)NULL), str);
++
++    if (value != NULL)
++        duplicateBuffer(str, value);
++    if (display_value != NULL)
++        duplicateBuffer(str, display_value);
++
++    *more = 0;
++
++    return true;
++}
++
++gss_any_t
++gss_eap_saml_assertion_provider::mapToAny(int authenticated,
++                                          gss_buffer_t type_id GSSEAP_UNUSED) const
++{
++    if (authenticated && !m_authenticated)
++        return (gss_any_t)NULL;
++
++    return (gss_any_t)m_assertion;
++}
++
++void
++gss_eap_saml_assertion_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
++                                                       gss_any_t input) const
++{
++    delete ((saml2::Assertion *)input);
++}
++
++const char *
++gss_eap_saml_assertion_provider::prefix(void) const
++{
++    return "urn:ietf:params:gss-eap:saml-aaa-assertion";
++}
++
++bool
++gss_eap_saml_assertion_provider::init(void)
++{
++    bool ret = false;
++
++    try {
++        ret = SAMLConfig::getConfig().init();
++    } catch (exception &e) {
++    }
++
++    if (ret)
++        gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML_ASSERTION, createAttrContext);
++
++    return ret;
++}
++
++void
++gss_eap_saml_assertion_provider::finalize(void)
++{
++    gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML_ASSERTION);
++}
++
++gss_eap_attr_provider *
++gss_eap_saml_assertion_provider::createAttrContext(void)
++{
++    return new gss_eap_saml_assertion_provider;
++}
++
++saml2::Assertion *
++gss_eap_saml_assertion_provider::initAssertion(void)
++{
++    delete m_assertion;
++    m_assertion = saml2::AssertionBuilder::buildAssertion();
++    m_authenticated = false;
++
++    return m_assertion;
++}
++
++/*
++ * gss_eap_saml_attr_provider is for retrieving the underlying attributes.
++ */
++bool
++gss_eap_saml_attr_provider::getAssertion(int *authenticated,
++                                         saml2::Assertion **pAssertion,
++                                         bool createIfAbsent) const
++{
++    gss_eap_saml_assertion_provider *saml;
++
++    if (authenticated != NULL)
++        *authenticated = false;
++    if (pAssertion != NULL)
++        *pAssertion = NULL;
++
++    saml = static_cast<gss_eap_saml_assertion_provider *>
++        (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION));
++    if (saml == NULL)
++        return false;
++
++    if (authenticated != NULL)
++        *authenticated = saml->authenticated();
++    if (pAssertion != NULL)
++        *pAssertion = saml->getAssertion();
++
++    if (saml->getAssertion() == NULL) {
++        if (createIfAbsent) {
++            if (authenticated != NULL)
++                *authenticated = false;
++            if (pAssertion != NULL)
++                *pAssertion = saml->initAssertion();
++        } else
++            return false;
++    }
++
++    return true;
++}
++
++bool
++gss_eap_saml_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
++                                              void *data) const
++{
++    saml2::Assertion *assertion;
++    int authenticated;
++
++    if (!getAssertion(&authenticated, &assertion))
++        return true;
++
++    /*
++     * Note: the first prefix is added by the attribute provider manager
++     *
++     * From draft-hartman-gss-eap-naming-00:
++     *
++     *   Each attribute carried in the assertion SHOULD also be a GSS name
++     *   attribute.  The name of this attribute has three parts, all separated
++     *   by an ASCII space character.  The first part is
++     *   urn:ietf:params:gss-eap:saml-attr.  The second part is the URI for
++     *   the SAML attribute name format.  The final part is the name of the
++     *   SAML attribute.  If the mechanism performs an additional attribute
++     *   query, the retrieved attributes SHOULD be GSS-API name attributes
++     *   using the same name syntax.
++     */
++    /* For each attribute statement, look for an attribute match */
++    const vector <saml2::AttributeStatement *> &statements =
++        const_cast<const saml2::Assertion *>(assertion)->getAttributeStatements();
++
++    for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
++        s != statements.end();
++        ++s) {
++        const vector<saml2::Attribute*> &attrs =
++            const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
++
++        for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
++            const XMLCh *attributeName, *attributeNameFormat;
++            XMLCh space[2] = { ' ', 0 };
++            gss_buffer_desc utf8;
++
++            attributeName = (*a)->getName();
++            attributeNameFormat = (*a)->getNameFormat();
++            if (attributeNameFormat == NULL || attributeNameFormat[0] == '\0')
++                attributeNameFormat = saml2::Attribute::UNSPECIFIED;
++
++            XMLCh qualifiedName[XMLString::stringLen(attributeNameFormat) + 1 +
++                                XMLString::stringLen(attributeName) + 1];
++            XMLString::copyString(qualifiedName, attributeNameFormat);
++            XMLString::catString(qualifiedName, space);
++            XMLString::catString(qualifiedName, attributeName);
++
++            utf8.value = (void *)toUTF8(qualifiedName);
++            utf8.length = strlen((char *)utf8.value);
++
++            if (!addAttribute(m_manager, this, &utf8, data))
++                return false;
++        }
++    }
++
++    return true;
++}
++
++static BaseRefVectorOf<XMLCh> *
++decomposeAttributeName(const gss_buffer_t attr)
++{
++    BaseRefVectorOf<XMLCh> *components;
++    string str((const char *)attr->value, attr->length);
++    auto_ptr_XMLCh qualifiedAttr(str.c_str());
++
++    components = XMLString::tokenizeString(qualifiedAttr.get());
++
++    if (components->size() != 2) {
++        delete components;
++        components = NULL;
++    }
++
++    return components;
++}
++
++bool
++gss_eap_saml_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
++                                         const gss_buffer_t attr,
++                                         const gss_buffer_t value)
++{
++    saml2::Assertion *assertion;
++    saml2::Attribute *attribute;
++    saml2::AttributeValue *attributeValue;
++    saml2::AttributeStatement *attributeStatement;
++
++    if (!getAssertion(NULL, &assertion, true))
++        return false;
++
++    if (assertion->getAttributeStatements().size() != 0) {
++        attributeStatement = assertion->getAttributeStatements().front();
++    } else {
++        attributeStatement = saml2::AttributeStatementBuilder::buildAttributeStatement();
++        assertion->getAttributeStatements().push_back(attributeStatement);
++    }
++
++    /* Check the attribute name consists of name format | whsp | name */
++    BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
++    if (components == NULL)
++        return false;
++
++    attribute = saml2::AttributeBuilder::buildAttribute();
++    attribute->setNameFormat(components->elementAt(0));
++    attribute->setName(components->elementAt(1));
++
++    attributeValue = saml2::AttributeValueBuilder::buildAttributeValue();
++    auto_ptr_XMLCh unistr((char *)value->value, value->length);
++    attributeValue->setTextContent(unistr.get());
++
++    attribute->getAttributeValues().push_back(attributeValue);
++
++    GSSEAP_ASSERT(attributeStatement != NULL);
++    attributeStatement->getAttributes().push_back(attribute);
++
++    delete components;
++
++    return true;
++}
++
++bool
++gss_eap_saml_attr_provider::deleteAttribute(const gss_buffer_t attr)
++{
++    saml2::Assertion *assertion;
++    bool ret = false;
++
++    if (!getAssertion(NULL, &assertion) ||
++        assertion->getAttributeStatements().size() == 0)
++        return false;
++
++    /* Check the attribute name consists of name format | whsp | name */
++    BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
++    if (components == NULL)
++        return false;
++
++    /* For each attribute statement, look for an attribute match */
++    const vector<saml2::AttributeStatement *> &statements =
++        const_cast<const saml2::Assertion *>(assertion)->getAttributeStatements();
++
++    for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
++        s != statements.end();
++        ++s) {
++        const vector<saml2::Attribute *> &attrs =
++            const_cast<const saml2::AttributeStatement *>(*s)->getAttributes();
++        ssize_t index = -1, i = 0;
++
++        /* There's got to be an easier way to do this */
++        for (vector<saml2::Attribute *>::const_iterator a = attrs.begin();
++             a != attrs.end();
++             ++a) {
++            if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) &&
++                XMLString::equals((*a)->getName(), components->elementAt(1))) {
++                index = i;
++                break;
++            }
++            ++i;
++        }
++        if (index != -1) {
++            (*s)->getAttributes().erase((*s)->getAttributes().begin() + index);
++            ret = true;
++        }
++    }
++
++    delete components;
++
++    return ret;
++}
++
++bool
++gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
++                                         int *authenticated,
++                                         int *complete,
++                                         const saml2::Attribute **pAttribute) const
++{
++    saml2::Assertion *assertion;
++
++    if (authenticated != NULL)
++        *authenticated = false;
++    if (complete != NULL)
++        *complete = true;
++    *pAttribute = NULL;
++
++    if (!getAssertion(authenticated, &assertion) ||
++        assertion->getAttributeStatements().size() == 0)
++        return false;
++
++    /* Check the attribute name consists of name format | whsp | name */
++    BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
++    if (components == NULL)
++        return false;
++
++    /* For each attribute statement, look for an attribute match */
++    const vector <saml2::AttributeStatement *> &statements =
++        const_cast<const saml2::Assertion *>(assertion)->getAttributeStatements();
++    const saml2::Attribute *ret = NULL;
++
++    for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
++        s != statements.end();
++        ++s) {
++        const vector<saml2::Attribute *> &attrs =
++            const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
++
++        for (vector<saml2::Attribute *>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
++            const XMLCh *attributeName, *attributeNameFormat;
++
++            attributeName = (*a)->getName();
++            attributeNameFormat = (*a)->getNameFormat();
++            if (attributeNameFormat == NULL || attributeNameFormat[0] == '\0')
++                attributeNameFormat = saml2::Attribute::UNSPECIFIED;
++
++            if (XMLString::equals(attributeNameFormat, components->elementAt(0)) &&
++                XMLString::equals(attributeName, components->elementAt(1))) {
++                ret = *a;
++                break;
++            }
++        }
++
++        if (ret != NULL)
++            break;
++    }
++
++    delete components;
++
++    *pAttribute = ret;
++
++    return (ret != NULL);
++}
++
++static bool
++isBase64EncodedAttributeValueP(const saml2::AttributeValue *av)
++{
++    const xmltooling::QName *type = av->getSchemaType();
++
++    if (type == NULL)
++        return false;
++
++    if (!type->hasNamespaceURI() ||
++        !XMLString::equals(type->getNamespaceURI(), xmlconstants::XSD_NS))
++        return false;
++
++    if (!type->hasLocalPart() ||
++        !XMLString::equals(type->getLocalPart(), base64Binary))
++        return false;
++
++    return true;
++}
++
++bool
++gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
++                                         int *authenticated,
++                                         int *complete,
++                                         gss_buffer_t value,
++                                         gss_buffer_t display_value,
++                                         int *more) const
++{
++    const saml2::Attribute *a;
++    const saml2::AttributeValue *av;
++    int nvalues, i = *more;
++
++    *more = 0;
++
++    if (!getAttribute(attr, authenticated, complete, &a))
++        return false;
++
++    nvalues = a->getAttributeValues().size();
++
++    if (i == -1)
++        i = 0;
++    if (i >= nvalues)
++        return false;
++#ifdef __APPLE__
++    av = (const saml2::AttributeValue *)((void *)(a->getAttributeValues().at(i)));
++#else
++    av = dynamic_cast<const saml2::AttributeValue *>(a->getAttributeValues().at(i));
++#endif
++    if (av != NULL) {
++        bool base64Encoded = isBase64EncodedAttributeValueP(av);
++
++        if (value != NULL) {
++            char *stringValue = toUTF8(av->getTextContent(), true);
++            size_t stringValueLen = strlen(stringValue);
++
++            if (base64Encoded) {
++                ssize_t octetLen;
++
++                value->value = GSSEAP_MALLOC(stringValueLen);
++                if (value->value == NULL) {
++                    GSSEAP_FREE(stringValue);
++                    throw new std::bad_alloc;
++                }
++
++                octetLen = base64Decode(stringValue, value->value);
++                if (octetLen < 0) {
++                    GSSEAP_FREE(value->value);
++                    GSSEAP_FREE(stringValue);
++                    value->value = NULL;
++                    return false;
++                }
++                value->length = octetLen;
++                GSSEAP_FREE(stringValue);
++            } else {
++                value->value = stringValue;
++                value->length = stringValueLen;
++            }
++        }
++        if (display_value != NULL && base64Encoded == false) {
++            display_value->value = toUTF8(av->getTextContent(), true);
++            display_value->length = strlen((char *)value->value);
++        }
++    }
++
++    if (nvalues > ++i)
++        *more = i;
++
++    return true;
++}
++
++gss_any_t
++gss_eap_saml_attr_provider::mapToAny(int authenticated GSSEAP_UNUSED,
++                                     gss_buffer_t type_id GSSEAP_UNUSED) const
++{
++    return (gss_any_t)NULL;
++}
++
++void
++gss_eap_saml_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
++                                                  gss_any_t input GSSEAP_UNUSED) const
++{
++}
++
++const char *
++gss_eap_saml_attr_provider::prefix(void) const
++{
++    return "urn:ietf:params:gss-eap:saml-attr";
++}
++
++bool
++gss_eap_saml_attr_provider::init(void)
++{
++    gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML, createAttrContext);
++    return true;
++}
++
++void
++gss_eap_saml_attr_provider::finalize(void)
++{
++    gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML);
++}
++
++gss_eap_attr_provider *
++gss_eap_saml_attr_provider::createAttrContext(void)
++{
++    return new gss_eap_saml_attr_provider;
++}
++
++OM_uint32
++gssEapSamlAttrProvidersInit(OM_uint32 *minor)
++{
++    if (!gss_eap_saml_assertion_provider::init() ||
++        !gss_eap_saml_attr_provider::init()) {
++        *minor = GSSEAP_SAML_INIT_FAILURE;
++        return GSS_S_FAILURE;
++    }
++
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapSamlAttrProvidersFinalize(OM_uint32 *minor)
++{
++    gss_eap_saml_attr_provider::finalize();
++    gss_eap_saml_assertion_provider::finalize();
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/util_saml.h b/mech_eap/util_saml.h
+new file mode 100644
+index 0000000..9110ad4
+--- /dev/null
++++ b/mech_eap/util_saml.h
+@@ -0,0 +1,176 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * SAML attribute provider.
++ */
++
++#ifndef _UTIL_SAML_H_
++#define _UTIL_SAML_H_ 1
++
++#ifdef __cplusplus
++
++namespace opensaml {
++    namespace saml2 {
++        class Attribute;
++        class Assertion;
++        class NameID;
++    };
++};
++
++struct gss_eap_saml_assertion_provider : gss_eap_attr_provider {
++public:
++    gss_eap_saml_assertion_provider(void);
++    ~gss_eap_saml_assertion_provider(void);
++
++    bool initWithExistingContext(const gss_eap_attr_ctx *source,
++                                 const gss_eap_attr_provider *ctx);
++    bool initWithGssContext(const gss_eap_attr_ctx *source,
++                            const gss_cred_id_t cred,
++                            const gss_ctx_id_t ctx);
++
++    bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const;
++    bool setAttribute(int complete,
++                      const gss_buffer_t attr,
++                      const gss_buffer_t value);
++    bool deleteAttribute(const gss_buffer_t value);
++    bool getAttribute(const gss_buffer_t attr,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    gss_any_t mapToAny(int authenticated,
++                       gss_buffer_t type_id) const;
++    void releaseAnyNameMapping(gss_buffer_t type_id,
++                               gss_any_t input) const;
++
++    const char *prefix(void) const;
++    const char *name(void) const { return NULL; }
++    bool initWithJsonObject(const gss_eap_attr_ctx *manager GSSEAP_UNUSED,
++                           JSONObject &object GSSEAP_UNUSED) {
++        return false;
++    }
++    JSONObject jsonRepresentation(void) const {
++        return JSONObject::null();
++    }
++
++    opensaml::saml2::Assertion *initAssertion(void);
++
++    opensaml::saml2::Assertion *getAssertion(void) const {
++        return m_assertion;
++    }
++    bool authenticated(void) const {
++        return m_authenticated;
++    }
++
++    time_t getExpiryTime(void) const;
++    OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const;
++
++    static bool init(void);
++    static void finalize(void);
++
++    static gss_eap_attr_provider *createAttrContext(void);
++
++private:
++    static opensaml::saml2::Assertion *
++        parseAssertion(const gss_buffer_t buffer);
++
++    void setAssertion(const opensaml::saml2::Assertion *assertion,
++                      bool authenticated = false);
++    void setAssertion(const gss_buffer_t buffer,
++                      bool authenticated = false);
++
++    opensaml::saml2::Assertion *m_assertion;
++    bool m_authenticated;
++};
++
++struct gss_eap_saml_attr_provider : gss_eap_attr_provider {
++public:
++    gss_eap_saml_attr_provider(void) {}
++    ~gss_eap_saml_attr_provider(void) {}
++
++    bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const;
++    bool setAttribute(int complete,
++                      const gss_buffer_t attr,
++                      const gss_buffer_t value);
++    bool deleteAttribute(const gss_buffer_t value);
++    bool getAttribute(const gss_buffer_t attr,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    gss_any_t mapToAny(int authenticated,
++                       gss_buffer_t type_id) const;
++    void releaseAnyNameMapping(gss_buffer_t type_id,
++                               gss_any_t input) const;
++
++    const char *prefix(void) const;
++    const char *name(void) const {
++        return NULL;
++    }
++    bool initWithJsonObject(const gss_eap_attr_ctx *manager GSSEAP_UNUSED,
++                            JSONObject &object GSSEAP_UNUSED) {
++        return false;
++    }
++    JSONObject jsonRepresentation(void) const {
++        return JSONObject::null();
++    }
++
++    bool getAttribute(const gss_buffer_t attr,
++                      int *authenticated,
++                      int *complete,
++                      const opensaml::saml2::Attribute **pAttribute) const;
++    bool getAssertion(int *authenticated,
++                      opensaml::saml2::Assertion **pAssertion,
++                      bool createIfAbsent = false) const;
++
++    static bool init(void);
++    static void finalize(void);
++
++    static gss_eap_attr_provider *createAttrContext(void);
++
++private:
++};
++
++extern "C" {
++#endif
++
++OM_uint32 gssEapSamlAttrProvidersInit(OM_uint32 *minor);
++OM_uint32 gssEapSamlAttrProvidersFinalize(OM_uint32 *minor);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _UTIL_SAML_H_ */
+diff --git a/mech_eap/util_shib.cpp b/mech_eap/util_shib.cpp
+new file mode 100644
+index 0000000..f8c702b
+--- /dev/null
++++ b/mech_eap/util_shib.cpp
+@@ -0,0 +1,555 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 2001-2009 Internet2
++ *
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++/*
++ * Local attribute provider implementation.
++ */
++
++#include "gssapiP_eap.h"
++
++#include <xmltooling/XMLObject.h>
++#ifndef HAVE_OPENSAML
++#include <xmltooling/XMLToolingConfig.h>
++#include <xmltooling/util/ParserPool.h>
++#endif
++
++#include <saml/saml2/core/Assertions.h>
++
++#include <shibsp/exceptions.h>
++#include <shibsp/attribute/SimpleAttribute.h>
++#include <shibsp/attribute/BinaryAttribute.h>
++#include <shibsp/attribute/ScopedAttribute.h>
++#include <shibresolver/resolver.h>
++
++#include <sstream>
++
++using namespace shibsp;
++using namespace shibresolver;
++using namespace xmltooling;
++using namespace std;
++#ifdef HAVE_OPENSAML
++using namespace opensaml::saml2md;
++using namespace opensaml;
++#else
++using namespace xercesc;
++#endif
++
++gss_eap_shib_attr_provider::gss_eap_shib_attr_provider(void)
++{
++    m_initialized = false;
++    m_authenticated = false;
++}
++
++gss_eap_shib_attr_provider::~gss_eap_shib_attr_provider(void)
++{
++    for_each(m_attributes.begin(),
++             m_attributes.end(),
++             xmltooling::cleanup<Attribute>())
++        ;
++}
++
++bool
++gss_eap_shib_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *manager,
++                                                    const gss_eap_attr_provider *ctx)
++{
++    const gss_eap_shib_attr_provider *shib;
++
++    if (!gss_eap_attr_provider::initWithExistingContext(manager, ctx)) {
++        return false;
++    }
++
++    m_authenticated = false;
++
++    shib = static_cast<const gss_eap_shib_attr_provider *>(ctx);
++    if (shib != NULL) {
++        m_attributes = duplicateAttributes(shib->getAttributes());
++        m_authenticated = shib->authenticated();
++    }
++
++    m_initialized = true;
++
++    return true;
++}
++
++bool
++gss_eap_shib_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
++                                               const gss_cred_id_t gssCred,
++                                               const gss_ctx_id_t gssCtx)
++{
++    if (!gss_eap_attr_provider::initWithGssContext(manager, gssCred, gssCtx))
++        return false;
++
++    auto_ptr<ShibbolethResolver> resolver(ShibbolethResolver::create());
++
++    /*
++     * For now, leave ApplicationID defaulted.
++     * Later on, we could allow this via config option to the mechanism
++     * or rely on an SPRequest interface to pass in a URI identifying the
++     * acceptor.
++     */
++#if 0
++    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
++    if (gssCred != GSS_C_NO_CREDENTIAL &&
++        gssEapDisplayName(&minor, gssCred->name, &nameBuf, NULL) == GSS_S_COMPLETE) {
++        resolver->setApplicationID((const char *)nameBuf.value);
++        gss_release_buffer(&minor, &nameBuf);
++    }
++#endif
++
++    gss_buffer_desc mechName = GSS_C_EMPTY_BUFFER;
++    OM_uint32 major, minor;
++
++    major = gssEapExportNameInternal(&minor, gssCtx->initiatorName, &mechName,
++                                     EXPORT_NAME_FLAG_OID |
++                                     EXPORT_NAME_FLAG_COMPOSITE);
++    if (major == GSS_S_COMPLETE) {
++        resolver->addToken(&mechName);
++        gss_release_buffer(&minor, &mechName);
++    }
++
++#ifdef HAVE_OPENSAML
++    const gss_eap_saml_assertion_provider *saml;
++    saml = static_cast<const gss_eap_saml_assertion_provider *>
++        (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION));
++    if (saml != NULL && saml->getAssertion() != NULL) {
++        resolver->addToken(saml->getAssertion());
++    }
++#else
++    /* If no OpenSAML, parse the XML assertion explicitly */
++    const gss_eap_radius_attr_provider *radius;
++    int authenticated, complete;
++    gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
++
++    radius = static_cast<const gss_eap_radius_attr_provider *>
++        (m_manager->getProvider(ATTR_TYPE_RADIUS));
++    if (radius != NULL &&
++        radius->getFragmentedAttribute(PW_SAML_AAA_ASSERTION,
++                                       VENDORPEC_UKERNA,
++                                       &authenticated, &complete, &value)) {
++        string str((char *)value.value, value.length);
++        istringstream istream(str);
++        DOMDocument *doc = XMLToolingConfig::getConfig().getParser().parse(istream);
++        const XMLObjectBuilder *b = XMLObjectBuilder::getBuilder(doc->getDocumentElement());
++        resolver->addToken(b->buildFromDocument(doc));
++        gss_release_buffer(&minor, &value);
++    }
++#endif /* HAVE_OPENSAML */
++
++    try {
++        resolver->resolve();
++        m_attributes = resolver->getResolvedAttributes();
++        resolver->getResolvedAttributes().clear();
++    } catch (exception &e) {
++        return false;
++    }
++
++    m_authenticated = true;
++    m_initialized = true;
++
++    return true;
++}
++
++ssize_t
++gss_eap_shib_attr_provider::getAttributeIndex(const gss_buffer_t attr) const
++{
++    int i = 0;
++
++    GSSEAP_ASSERT(m_initialized);
++
++    for (vector<Attribute *>::const_iterator a = m_attributes.begin();
++         a != m_attributes.end();
++         ++a)
++    {
++        for (vector<string>::const_iterator s = (*a)->getAliases().begin();
++             s != (*a)->getAliases().end();
++             ++s) {
++            if (attr->length == (*s).length() &&
++                memcmp((*s).c_str(), attr->value, attr->length) == 0) {
++                return i;
++            }
++        }
++    }
++
++    return -1;
++}
++
++bool
++gss_eap_shib_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
++                                         const gss_buffer_t attr,
++                                         const gss_buffer_t value)
++{
++    string attrStr((char *)attr->value, attr->length);
++    vector <string> ids(1, attrStr);
++    BinaryAttribute *a = new BinaryAttribute(ids);
++
++    GSSEAP_ASSERT(m_initialized);
++
++    if (value->length != 0) {
++        string valueStr((char *)value->value, value->length);
++
++        a->getValues().push_back(valueStr);
++    }
++
++    m_attributes.push_back(a);
++    m_authenticated = false;
++
++    return true;
++}
++
++bool
++gss_eap_shib_attr_provider::deleteAttribute(const gss_buffer_t attr)
++{
++    int i;
++
++    GSSEAP_ASSERT(m_initialized);
++
++    i = getAttributeIndex(attr);
++    if (i >= 0)
++        m_attributes.erase(m_attributes.begin() + i);
++
++    m_authenticated = false;
++
++    return true;
++}
++
++bool
++gss_eap_shib_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
++                                              void *data) const
++{
++    GSSEAP_ASSERT(m_initialized);
++
++    for (vector<Attribute*>::const_iterator a = m_attributes.begin();
++        a != m_attributes.end();
++        ++a)
++    {
++        gss_buffer_desc attribute;
++
++        attribute.value = (void *)((*a)->getId());
++        attribute.length = strlen((char *)attribute.value);
++
++        if (!addAttribute(m_manager, this, &attribute, data))
++            return false;
++    }
++
++    return true;
++}
++
++const Attribute *
++gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr) const
++{
++    const Attribute *ret = NULL;
++
++    GSSEAP_ASSERT(m_initialized);
++
++    for (vector<Attribute *>::const_iterator a = m_attributes.begin();
++         a != m_attributes.end();
++         ++a)
++    {
++        for (vector<string>::const_iterator s = (*a)->getAliases().begin();
++             s != (*a)->getAliases().end();
++             ++s) {
++            if (attr->length == (*s).length() &&
++                memcmp((*s).c_str(), attr->value, attr->length) == 0) {
++                ret = *a;
++                break;
++            }
++        }
++        if (ret != NULL)
++            break;
++    }
++
++    return ret;
++}
++
++bool
++gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr,
++                                         int *authenticated,
++                                         int *complete,
++                                         gss_buffer_t value,
++                                         gss_buffer_t display_value,
++                                         int *more) const
++{
++    const Attribute *shibAttr = NULL;
++    const BinaryAttribute *binaryAttr;
++    gss_buffer_desc valueBuf = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc displayValueBuf = GSS_C_EMPTY_BUFFER;
++    int nvalues, i = *more;
++
++    GSSEAP_ASSERT(m_initialized);
++
++    *more = 0;
++
++    shibAttr = getAttribute(attr);
++    if (shibAttr == NULL)
++        return false;
++
++    nvalues = shibAttr->valueCount();
++
++    if (i == -1)
++        i = 0;
++    if (i >= nvalues)
++        return false;
++
++    binaryAttr = dynamic_cast<const BinaryAttribute *>(shibAttr);
++    if (binaryAttr != NULL) {
++        std::string str = binaryAttr->getValues()[*more];
++
++        valueBuf.value = (void *)str.data();
++        valueBuf.length = str.size();
++    } else {
++        std::string str = shibAttr->getSerializedValues()[*more];
++
++        valueBuf.value = (void *)str.c_str();
++        valueBuf.length = str.length();
++
++        const SimpleAttribute *simpleAttr =
++            dynamic_cast<const SimpleAttribute *>(shibAttr);
++        const ScopedAttribute *scopedAttr =
++            dynamic_cast<const ScopedAttribute *>(shibAttr);
++        if (simpleAttr != NULL || scopedAttr != NULL)
++            displayValueBuf = valueBuf;
++    }
++
++    if (authenticated != NULL)
++        *authenticated = m_authenticated;
++    if (complete != NULL)
++        *complete = true;
++    if (value != NULL)
++        duplicateBuffer(valueBuf, value);
++    if (display_value != NULL)
++        duplicateBuffer(displayValueBuf, display_value);
++    if (nvalues > ++i)
++        *more = i;
++
++    return true;
++}
++
++gss_any_t
++gss_eap_shib_attr_provider::mapToAny(int authenticated,
++                                     gss_buffer_t type_id GSSEAP_UNUSED) const
++{
++    gss_any_t output;
++
++    GSSEAP_ASSERT(m_initialized);
++
++    if (authenticated && !m_authenticated)
++        return (gss_any_t)NULL;
++
++    vector <Attribute *>v = duplicateAttributes(m_attributes);
++
++    output = (gss_any_t)new vector <Attribute *>(v);
++
++    return output;
++}
++
++void
++gss_eap_shib_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
++                                                  gss_any_t input) const
++{
++    GSSEAP_ASSERT(m_initialized);
++
++    vector <Attribute *> *v = ((vector <Attribute *> *)input);
++    delete v;
++}
++
++const char *
++gss_eap_shib_attr_provider::prefix(void) const
++{
++    return NULL;
++}
++
++const char *
++gss_eap_shib_attr_provider::name(void) const
++{
++    return "local";
++}
++
++JSONObject
++gss_eap_shib_attr_provider::jsonRepresentation(void) const
++{
++    JSONObject obj;
++
++    if (m_initialized == false)
++        return obj; /* don't export incomplete context */
++
++    JSONObject jattrs = JSONObject::array();
++
++    for (vector<Attribute*>::const_iterator a = m_attributes.begin();
++         a != m_attributes.end(); ++a) {
++        DDF attr = (*a)->marshall();
++        JSONObject jattr = JSONObject::ddf(attr);
++        jattrs.append(jattr);
++    }
++
++    obj.set("attributes", jattrs);
++
++    obj.set("authenticated", m_authenticated);
++
++    return obj;
++}
++
++bool
++gss_eap_shib_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
++                                               JSONObject &obj)
++{
++    if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
++        return false;
++
++    GSSEAP_ASSERT(m_authenticated == false);
++    GSSEAP_ASSERT(m_attributes.size() == 0);
++
++    JSONObject jattrs = obj["attributes"];
++    size_t nelems = jattrs.size();
++
++    for (size_t i = 0; i < nelems; i++) {
++        JSONObject jattr = jattrs.get(i);
++
++        DDF attr = jattr.ddf();
++        Attribute *attribute = Attribute::unmarshall(attr);
++        m_attributes.push_back(attribute);
++    }
++
++    m_authenticated = obj["authenticated"].integer();
++    m_initialized = true;
++
++    return true;
++}
++
++bool
++gss_eap_shib_attr_provider::init(void)
++{
++    bool ret = false;
++
++    try {
++        ret = ShibbolethResolver::init();
++    } catch (exception &e) {
++    }
++
++    if (ret)
++        gss_eap_attr_ctx::registerProvider(ATTR_TYPE_LOCAL, createAttrContext);
++
++    return ret;
++}
++
++void
++gss_eap_shib_attr_provider::finalize(void)
++{
++    gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_LOCAL);
++    ShibbolethResolver::term();
++}
++
++OM_uint32
++gss_eap_shib_attr_provider::mapException(OM_uint32 *minor,
++                                         std::exception &e) const
++{
++    if (typeid(e) == typeid(AttributeException))
++        *minor = GSSEAP_SHIB_ATTR_FAILURE;
++    else if (typeid(e) == typeid(AttributeExtractionException))
++        *minor = GSSEAP_SHIB_ATTR_EXTRACT_FAILURE;
++    else if (typeid(e) == typeid(AttributeFilteringException))
++        *minor = GSSEAP_SHIB_ATTR_FILTER_FAILURE;
++    else if (typeid(e) == typeid(AttributeResolutionException))
++        *minor = GSSEAP_SHIB_ATTR_RESOLVE_FAILURE;
++    else if (typeid(e) == typeid(ConfigurationException))
++        *minor = GSSEAP_SHIB_CONFIG_FAILURE;
++    else if (typeid(e) == typeid(ListenerException))
++        *minor = GSSEAP_SHIB_LISTENER_FAILURE;
++    else
++        return GSS_S_CONTINUE_NEEDED;
++
++    gssEapSaveStatusInfo(*minor, "%s", e.what());
++
++    return GSS_S_FAILURE;
++}
++
++gss_eap_attr_provider *
++gss_eap_shib_attr_provider::createAttrContext(void)
++{
++    return new gss_eap_shib_attr_provider;
++}
++
++Attribute *
++gss_eap_shib_attr_provider::duplicateAttribute(const Attribute *src)
++{
++    DDF obj = src->marshall();
++    Attribute *attribute = Attribute::unmarshall(obj);
++    obj.destroy();
++
++    return attribute;
++}
++
++vector <Attribute *>
++gss_eap_shib_attr_provider::duplicateAttributes(const vector <Attribute *>src)
++{
++    vector <Attribute *> dst;
++
++    for (vector<Attribute *>::const_iterator a = src.begin();
++         a != src.end();
++         ++a)
++        dst.push_back(duplicateAttribute(*a));
++
++    return dst;
++}
++
++OM_uint32
++gssEapLocalAttrProviderInit(OM_uint32 *minor)
++{
++    if (!gss_eap_shib_attr_provider::init()) {
++        *minor = GSSEAP_SHIB_INIT_FAILURE;
++        return GSS_S_FAILURE;
++    }
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapLocalAttrProviderFinalize(OM_uint32 *minor)
++{
++    gss_eap_shib_attr_provider::finalize();
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/util_shib.h b/mech_eap/util_shib.h
+new file mode 100644
+index 0000000..4cf7481
+--- /dev/null
++++ b/mech_eap/util_shib.h
+@@ -0,0 +1,122 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Local attribute provider.
++ */
++
++#ifndef _UTIL_SHIB_H_
++#define _UTIL_SHIB_H_ 1
++
++#ifdef __cplusplus
++
++#include <vector>
++
++namespace shibsp {
++    class Attribute;
++};
++
++namespace shibresolver {
++    class ShibbolethResolver;
++};
++
++struct gss_eap_shib_attr_provider : gss_eap_attr_provider {
++public:
++    gss_eap_shib_attr_provider(void);
++    ~gss_eap_shib_attr_provider(void);
++
++    bool initWithExistingContext(const gss_eap_attr_ctx *source,
++                                 const gss_eap_attr_provider *ctx);
++    bool initWithGssContext(const gss_eap_attr_ctx *source,
++                            const gss_cred_id_t cred,
++                            const gss_ctx_id_t ctx);
++
++    bool setAttribute(int complete,
++                      const gss_buffer_t attr,
++                      const gss_buffer_t value);
++    bool deleteAttribute(const gss_buffer_t value);
++    bool getAttributeTypes(gss_eap_attr_enumeration_cb, void *data) const;
++    bool getAttribute(const gss_buffer_t attr,
++                      int *authenticated,
++                      int *complete,
++                      gss_buffer_t value,
++                      gss_buffer_t display_value,
++                      int *more) const;
++    gss_any_t mapToAny(int authenticated,
++                       gss_buffer_t type_id) const;
++    void releaseAnyNameMapping(gss_buffer_t type_id,
++                               gss_any_t input) const;
++
++    const char *prefix(void) const;
++    const char *name(void) const;
++    bool initWithJsonObject(const gss_eap_attr_ctx *manager,
++                            JSONObject &obj);
++    JSONObject jsonRepresentation(void) const;
++
++    static bool init(void);
++    static void finalize(void);
++
++    OM_uint32 mapException(OM_uint32 *minor, std::exception &e) const;
++
++    static gss_eap_attr_provider *createAttrContext(void);
++
++    std::vector<shibsp::Attribute *> getAttributes(void) const {
++        return m_attributes;
++    }
++
++private:
++    static shibsp::Attribute *
++        duplicateAttribute(const shibsp::Attribute *src);
++    static std::vector <shibsp::Attribute *>
++        duplicateAttributes(const std::vector <shibsp::Attribute *>src);
++
++    ssize_t getAttributeIndex(const gss_buffer_t attr) const;
++    const shibsp::Attribute *getAttribute(const gss_buffer_t attr) const;
++
++    bool authenticated(void) const { return m_authenticated; }
++
++    bool m_initialized;
++    bool m_authenticated;
++    std::vector<shibsp::Attribute *> m_attributes;
++};
++
++extern "C" {
++#endif
++
++OM_uint32 gssEapLocalAttrProviderInit(OM_uint32 *minor);
++OM_uint32 gssEapLocalAttrProviderFinalize(OM_uint32 *minor);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _UTIL_SHIB_H_ */
+diff --git a/mech_eap/util_sm.c b/mech_eap/util_sm.c
+new file mode 100644
+index 0000000..56248d8
+--- /dev/null
++++ b/mech_eap/util_sm.c
+@@ -0,0 +1,372 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Context establishment state machine.
++ */
++
++#include "gssapiP_eap.h"
++
++/* private flags */
++#define SM_FLAG_TRANSITED                   0x80000000
++
++#define SM_ASSERT_VALID(ctx, status)        do { \
++        GSSEAP_ASSERT(GSS_ERROR((status)) || \
++               ((status) == GSS_S_CONTINUE_NEEDED && ((ctx)->state > GSSEAP_STATE_INITIAL && (ctx)->state < GSSEAP_STATE_ESTABLISHED)) || \
++               ((status) == GSS_S_COMPLETE && (ctx)->state == GSSEAP_STATE_ESTABLISHED)); \
++    } while (0)
++
++#ifdef GSSEAP_DEBUG
++static const char *
++gssEapStateToString(enum gss_eap_state state)
++{
++    const char *s;
++
++    switch (state) {
++    case GSSEAP_STATE_INITIAL:
++        s = "INITIAL";
++        break;
++    case GSSEAP_STATE_AUTHENTICATE:
++        s = "AUTHENTICATE";
++        break;
++    case GSSEAP_STATE_INITIATOR_EXTS:
++        s = "INITIATOR_EXTS";
++        break;
++    case GSSEAP_STATE_ACCEPTOR_EXTS:
++        s = "ACCEPTOR_EXTS";
++        break;
++#ifdef GSSEAP_ENABLE_REAUTH
++    case GSSEAP_STATE_REAUTHENTICATE:
++        s = "REAUTHENTICATE";
++        break;
++#endif
++    case GSSEAP_STATE_ESTABLISHED:
++        s = "ESTABLISHED";
++        break;
++    default:
++        s = "INVALID";
++        break;
++    }
++
++    return s;
++}
++
++void
++gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
++{
++    GSSEAP_ASSERT(state >= GSSEAP_STATE_INITIAL);
++    GSSEAP_ASSERT(state <= GSSEAP_STATE_ESTABLISHED);
++
++    fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
++            gssEapStateToString(GSSEAP_SM_STATE(ctx)),
++            gssEapStateToString(state));
++
++    ctx->state = state;
++}
++#endif /* GSSEAP_DEBUG */
++
++static OM_uint32
++makeErrorToken(OM_uint32 *minor,
++               OM_uint32 majorStatus,
++               OM_uint32 minorStatus,
++               struct gss_eap_token_buffer_set *token)
++{
++    OM_uint32 major, tmpMinor;
++    unsigned char errorData[8];
++    gss_buffer_desc errorBuffer;
++
++    GSSEAP_ASSERT(GSS_ERROR(majorStatus));
++
++    /*
++     * Only return error codes that the initiator could have caused,
++     * to avoid information leakage.
++     */
++    if (IS_RADIUS_ERROR(minorStatus)) {
++        /* Squash RADIUS error codes */
++        minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
++    } else if (!IS_WIRE_ERROR(minorStatus)) {
++        /* Don't return non-wire error codes */
++        return GSS_S_COMPLETE;
++    }
++
++    minorStatus -= ERROR_TABLE_BASE_eapg;
++
++    store_uint32_be(majorStatus, &errorData[0]);
++    store_uint32_be(minorStatus, &errorData[4]);
++
++    major = gssEapAllocInnerTokens(&tmpMinor, 1, token);
++    if (GSS_ERROR(major)) {
++        *minor = tmpMinor;
++        return major;
++    }
++
++    errorBuffer.length = sizeof(errorData);
++    errorBuffer.value = errorData;
++
++    major = duplicateBuffer(&tmpMinor, &errorBuffer, &token->buffers.elements[0]);
++    if (GSS_ERROR(major)) {
++        gssEapReleaseInnerTokens(&tmpMinor, token, 1);
++        *minor = tmpMinor;
++        return major;
++    }
++
++    token->buffers.count = 1;
++    token->types[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapSmStep(OM_uint32 *minor,
++             gss_cred_id_t cred,
++             gss_ctx_id_t ctx,
++             gss_name_t target,
++             gss_OID mech,
++             OM_uint32 reqFlags,
++             OM_uint32 timeReq,
++             gss_channel_bindings_t chanBindings,
++             gss_buffer_t inputToken,
++             gss_buffer_t outputToken,
++             struct gss_eap_sm *sm, /* ordered by state */
++             size_t smCount)
++{
++    OM_uint32 major, tmpMajor, tmpMinor;
++    struct gss_eap_token_buffer_set inputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
++    struct gss_eap_token_buffer_set outputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
++    gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
++    gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
++    unsigned int smFlags = 0;
++    size_t i, j;
++    int initialContextToken = 0;
++    enum gss_eap_token_type tokType;
++
++    GSSEAP_ASSERT(smCount > 0);
++
++    *minor = 0;
++
++    outputToken->length = 0;
++    outputToken->value = NULL;
++
++    if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
++        major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
++                                  &unwrappedInputToken);
++        if (GSS_ERROR(major))
++            goto cleanup;
++
++        if (tokType != (CTX_IS_INITIATOR(ctx)
++                    ? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT)) {
++            major = GSS_S_DEFECTIVE_TOKEN;
++            *minor = GSSEAP_WRONG_TOK_ID;
++            goto cleanup;
++        }
++    } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
++        major = GSS_S_DEFECTIVE_TOKEN;
++        *minor = GSSEAP_WRONG_SIZE;
++        goto cleanup;
++    } else {
++        initialContextToken = 1;
++    }
++
++    if (CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_BAD_STATUS;
++        *minor = GSSEAP_CONTEXT_ESTABLISHED;
++        goto cleanup;
++    }
++
++    GSSEAP_ASSERT(ctx->state < GSSEAP_STATE_ESTABLISHED);
++
++    major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, &inputTokens);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    major = gssEapAllocInnerTokens(minor, smCount, &outputTokens);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    ctx->inputTokens = &inputTokens;
++    ctx->outputTokens = &outputTokens;
++
++    /* Process all the tokens that are valid for the current state. */
++    for (i = 0; i < smCount; i++) {
++        struct gss_eap_sm *smp = &sm[i];
++        int processToken = 0;
++        gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
++        OM_uint32 *inputTokenType = NULL;
++        gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
++
++        if ((smp->validStates & ctx->state) == 0)
++            continue;
++
++        /*
++         * We special case the first call to gss_init_sec_context so that
++         * all token providers have the opportunity to generate an initial
++         * context token. Providers where inputTokenType is ITOK_TYPE_NONE
++         * are always called and generally act on state transition boundaries,
++         * for example to advance the state after a series of optional tokens
++         * (as is the case with the extension token exchange) or to generate
++         * a new token after the state was advanced by a provider which did
++         * not emit a token.
++         */
++        if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
++            processToken = 1;
++        } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
++            /* Don't regurgitate a token which belonds to a previous state. */
++            for (j = 0; j < inputTokens.buffers.count; j++) {
++                if ((inputTokens.types[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
++                    if (processToken) {
++                        /* Check for duplicate inner tokens */
++                        major = GSS_S_DEFECTIVE_TOKEN;
++                        *minor = GSSEAP_DUPLICATE_ITOK;
++                        break;
++                    }
++                    processToken = 1;
++                    innerInputToken = &inputTokens.buffers.elements[j];
++                    inputTokenType = &inputTokens.types[j];
++                }
++            }
++            if (GSS_ERROR(major))
++                break;
++        }
++
++        if (processToken) {
++            enum gss_eap_state oldState = ctx->state;
++
++            smFlags = 0;
++            if (inputTokenType != NULL && (*inputTokenType & ITOK_FLAG_CRITICAL))
++                smFlags |= SM_FLAG_INPUT_TOKEN_CRITICAL;
++
++            major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
++                                      timeReq, chanBindings, innerInputToken,
++                                      &innerOutputToken, &smFlags);
++            if (GSS_ERROR(major))
++                break;
++
++            if (inputTokenType != NULL)
++                *inputTokenType |= ITOK_FLAG_VERIFIED;
++            if (ctx->state < oldState)
++                i = 0; /* restart */
++            else if (ctx->state != oldState)
++                smFlags |= SM_FLAG_TRANSITED;
++
++            if (innerOutputToken.value != NULL) {
++                outputTokens.buffers.elements[outputTokens.buffers.count] = innerOutputToken;
++                GSSEAP_ASSERT(smp->outputTokenType != ITOK_TYPE_NONE);
++                outputTokens.types[outputTokens.buffers.count] = smp->outputTokenType;
++                if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
++                    outputTokens.types[outputTokens.buffers.count] |= ITOK_FLAG_CRITICAL;
++                outputTokens.buffers.count++;
++            }
++            /*
++             * Break out if we made a state transition and have some tokens to send.
++             */
++            if ((smFlags & SM_FLAG_TRANSITED) &&
++                 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || outputTokens.buffers.count != 0)) {
++                SM_ASSERT_VALID(ctx, major);
++                break;
++            }
++        } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
++            smp->inputTokenType != ITOK_TYPE_NONE) {
++            /* Check for required inner tokens */
++            major = GSS_S_DEFECTIVE_TOKEN;
++            *minor = GSSEAP_MISSING_REQUIRED_ITOK;
++            break;
++        }
++    }
++
++    GSSEAP_ASSERT(outputTokens.buffers.count <= smCount);
++
++    /* Check we understood all critical tokens sent by peer */
++    if (!GSS_ERROR(major)) {
++        for (j = 0; j < inputTokens.buffers.count; j++) {
++            if ((inputTokens.types[j] & ITOK_FLAG_CRITICAL) &&
++                (inputTokens.types[j] & ITOK_FLAG_VERIFIED) == 0) {
++                major = GSS_S_UNAVAILABLE;
++                *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
++                goto cleanup;
++            }
++        }
++    }
++
++    /* Optionaly emit an error token if we are the acceptor */
++    if (GSS_ERROR(major)) {
++        if (CTX_IS_INITIATOR(ctx))
++            goto cleanup; /* return error directly to caller */
++
++        /* replace any emitted tokens with error token */
++        gssEapReleaseInnerTokens(&tmpMinor, &outputTokens, 1);
++
++        tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &outputTokens);
++        if (GSS_ERROR(tmpMajor)) {
++            major = tmpMajor;
++            *minor = tmpMinor;
++            goto cleanup;
++        }
++    }
++
++    /* Format output token from inner tokens */
++    if (outputTokens.buffers.count != 0 ||            /* inner tokens to send */
++        !CTX_IS_INITIATOR(ctx) ||                   /* any leg acceptor */
++        !CTX_IS_ESTABLISHED(ctx)) {                 /* non-last leg initiator */
++        tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, &outputTokens, &unwrappedOutputToken);
++        if (tmpMajor == GSS_S_COMPLETE) {
++            if (CTX_IS_INITIATOR(ctx))
++                tokType = TOK_TYPE_INITIATOR_CONTEXT;
++            else
++                tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
++
++            tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
++                                       tokType, outputToken);
++            if (GSS_ERROR(tmpMajor)) {
++                major = tmpMajor;
++                *minor = tmpMinor;
++                goto cleanup;
++            }
++        }
++    }
++
++    /* If the context is established, empty tokens only to be emitted by initiator */
++    GSSEAP_ASSERT(!CTX_IS_ESTABLISHED(ctx) || ((outputToken->length == 0) == CTX_IS_INITIATOR(ctx)));
++
++    SM_ASSERT_VALID(ctx, major);
++
++cleanup:
++    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 0);
++    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 1);
++
++    gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
++
++    ctx->inputTokens = NULL;
++    ctx->outputTokens = NULL;
++
++    return major;
++}
+diff --git a/mech_eap/util_tld.c b/mech_eap/util_tld.c
+new file mode 100644
+index 0000000..05bc3d1
+--- /dev/null
++++ b/mech_eap/util_tld.c
+@@ -0,0 +1,167 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Thread local data abstraction, using pthreads on Unix and the TlsXXX
++ * APIs on Windows.
++ */
++
++#include "gssapiP_eap.h"
++
++/* Clean up thread-local data; called on thread detach */
++static void
++destroyThreadLocalData(struct gss_eap_thread_local_data *tld)
++{
++    if (tld->statusInfo != NULL)
++        gssEapDestroyStatusInfo(tld->statusInfo);
++    if (tld->krbContext != NULL)
++        gssEapDestroyKrbContext(tld->krbContext);
++    GSSEAP_FREE(tld);
++}
++
++#ifdef WIN32
++
++/*
++ * This is the TLS index returned by TlsAlloc() on process init.
++ * Each thread, on thread attach in DllMain(), allocates its thread-local
++ * data and uses this index with TlsSetValue() to store it.
++ * It can then subsequently be retrieved with TlsGetValue().
++ */
++static DWORD tlsIndex = TLS_OUT_OF_INDEXES;
++
++/* Access thread-local data */
++struct gss_eap_thread_local_data *
++gssEapGetThreadLocalData(void)
++{
++    struct gss_eap_thread_local_data *tlsData;
++
++    GSSEAP_ASSERT(tlsIndex != TLS_OUT_OF_INDEXES);
++
++    tlsData = TlsGetValue(tlsIndex);
++    if (tlsData == NULL) {
++        tlsData = GSSEAP_CALLOC(1, sizeof(*tlsData));
++        TlsSetValue(tlsIndex, tlsData);
++    }
++
++    return tlsData;
++}
++
++BOOL WINAPI
++DllMain(HINSTANCE hDLL,     /* DLL module handle */
++        DWORD reason,       /* reason called */
++        LPVOID reserved)    /* reserved */
++{
++    struct gss_eap_thread_local_data *tlsData;
++    OM_uint32 major, minor;
++
++    switch (reason) {
++        case DLL_PROCESS_ATTACH:
++            /* Allocate a TLS index. */
++            major = gssEapInitiatorInit(&minor);
++            if (GSS_ERROR(major))
++                return FALSE;
++
++            tlsIndex = TlsAlloc();
++            if (tlsIndex == TLS_OUT_OF_INDEXES)
++                return FALSE;
++            /* No break: Initialize the index for first thread.*/
++        case DLL_THREAD_ATTACH:
++            /* Initialize the TLS index for this thread. */
++            tlsData = GSSEAP_CALLOC(1, sizeof(*tlsData));
++            if (tlsData == NULL)
++                return FALSE;
++            TlsSetValue(tlsIndex, tlsData);
++            break;
++        case DLL_THREAD_DETACH:
++            /* Release the allocated memory for this thread. */
++            tlsData = TlsGetValue(tlsIndex);
++            if (tlsData != NULL) {
++                destroyThreadLocalData(tlsData);
++                TlsSetValue(tlsIndex, NULL);
++            }
++            break;
++        case DLL_PROCESS_DETACH:
++            /* Release the TLS index. */
++            TlsFree(tlsIndex);
++            gssEapFinalize();
++            break;
++        default:
++            break;
++    }
++
++    return TRUE;
++    UNREFERENCED_PARAMETER(hDLL);
++    UNREFERENCED_PARAMETER(reserved);
++}
++
++#else /* WIN32 */
++
++/* pthreads implementation */
++
++static GSSEAP_THREAD_ONCE tldKeyOnce = GSSEAP_ONCE_INITIALIZER;
++static GSSEAP_THREAD_KEY tldKey;
++
++static void
++pthreadDestroyThreadLocalData(void *arg)
++{
++    struct gss_eap_thread_local_data* tld = arg;
++
++    if (tld != NULL)
++        destroyThreadLocalData(tld);
++}
++
++static void
++createThreadLocalDataKey(void)
++{
++    GSSEAP_KEY_CREATE(&tldKey, pthreadDestroyThreadLocalData);
++}
++
++struct gss_eap_thread_local_data *
++gssEapGetThreadLocalData()
++{
++    struct gss_eap_thread_local_data *tld;
++
++    GSSEAP_ONCE(&tldKeyOnce, createThreadLocalDataKey);
++
++    tld = GSSEAP_GETSPECIFIC(tldKey);
++    if (tld == NULL) {
++        tld = GSSEAP_CALLOC(1, sizeof(*tld));
++        if (tld == NULL)
++            return NULL;
++
++        GSSEAP_SETSPECIFIC(tldKey, tld);
++    }
++
++    return tld;
++}
++
++#endif /* WIN32 */
+diff --git a/mech_eap/util_token.c b/mech_eap/util_token.c
+new file mode 100644
+index 0000000..a1aea0c
+--- /dev/null
++++ b/mech_eap/util_token.c
+@@ -0,0 +1,493 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Portions Copyright 1993 by OpenVision Technologies, Inc.
++ *
++ * Permission to use, copy, modify, distribute, and sell this software
++ * and its documentation for any purpose is hereby granted without fee,
++ * provided that the above copyright notice appears in all copies and
++ * that both that copyright notice and this permission notice appear in
++ * supporting documentation, and that the name of OpenVision not be used
++ * in advertising or publicity pertaining to distribution of the software
++ * without specific, written prior permission. OpenVision makes no
++ * representations about the suitability of this software for any
++ * purpose.  It is provided "as is" without express or implied warranty.
++ *
++ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
++ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
++ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++ * PERFORMANCE OF THIS SOFTWARE.
++ */
++
++/*
++ * Utility routines for GSS tokens.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32
++gssEapEncodeInnerTokens(OM_uint32 *minor,
++                        struct gss_eap_token_buffer_set *tokens,
++                        gss_buffer_t buffer)
++{
++    OM_uint32 major, tmpMinor;
++    size_t required = 0, i;
++    unsigned char *p;
++
++    buffer->value = NULL;
++    buffer->length = 0;
++
++    for (i = 0; i < tokens->buffers.count; i++) {
++        required += 8 + tokens->buffers.elements[i].length;
++    }
++
++    /*
++     * We must always return a non-NULL token otherwise the calling state
++     * machine assumes we are finished. Hence care in case malloc(0) does
++     * return NULL.
++     */
++    buffer->value = GSSEAP_MALLOC(required ? required : 1);
++    if (buffer->value == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++
++    buffer->length = required;
++    p = (unsigned char *)buffer->value;
++
++    for (i = 0; i < tokens->buffers.count; i++) {
++        gss_buffer_t tokenBuffer = &tokens->buffers.elements[i];
++
++        GSSEAP_ASSERT((tokens->types[i] & ITOK_FLAG_VERIFIED) == 0); /* private flag */
++
++         /*
++          * Extensions are encoded as type-length-value, where the upper
++          * bit of the type indicates criticality.
++          */
++        store_uint32_be(tokens->types[i], &p[0]);
++        store_uint32_be(tokenBuffer->length, &p[4]);
++        memcpy(&p[8], tokenBuffer->value, tokenBuffer->length);
++
++        p += 8 + tokenBuffer->length;
++    }
++
++    GSSEAP_ASSERT(p == (unsigned char *)buffer->value + required);
++    GSSEAP_ASSERT(buffer->value != NULL);
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        gss_release_buffer(&tmpMinor, buffer);
++    }
++
++    return major;
++}
++
++OM_uint32
++gssEapDecodeInnerTokens(OM_uint32 *minor,
++                        const gss_buffer_t buffer,
++                        struct gss_eap_token_buffer_set *tokens)
++{
++    OM_uint32 major, tmpMinor;
++    unsigned char *p;
++    size_t count = 0;
++    size_t remain;
++
++    tokens->buffers.count = 0;
++    tokens->buffers.elements = NULL;
++    tokens->types = NULL;
++
++    if (buffer->length == 0) {
++        major = GSS_S_COMPLETE;
++        goto cleanup;
++    }
++
++    p = (unsigned char *)buffer->value;
++    remain = buffer->length;
++
++    do {
++        OM_uint32 *ntypes;
++        gss_buffer_desc tokenBuffer, *newTokenBuffers;
++
++        if (remain < 8) {
++            major = GSS_S_DEFECTIVE_TOKEN;
++            *minor = GSSEAP_TOK_TRUNC;
++            goto cleanup;
++        }
++
++        if (tokens->buffers.count <= count) {
++            if (count == 0)
++                count = 1;
++            else
++                count *= 2;
++
++            ntypes = GSSEAP_MALLOC(count * sizeof(OM_uint32));
++            if (ntypes == NULL) {
++                major = GSS_S_FAILURE;
++                *minor = ENOMEM;
++                goto cleanup;
++            }
++            if (tokens->types != NULL) {
++                memcpy(ntypes, tokens->types, tokens->buffers.count * sizeof(OM_uint32));
++                GSSEAP_FREE(tokens->types);
++            }
++            tokens->types = ntypes;
++
++            newTokenBuffers = GSSEAP_MALLOC(count * sizeof(gss_buffer_desc));
++            if (newTokenBuffers == NULL) {
++                major = GSS_S_FAILURE;
++                *minor = ENOMEM;
++                goto cleanup;
++            }
++            if (tokens->buffers.elements != NULL) {
++                memcpy(newTokenBuffers, tokens->buffers.elements,
++                       tokens->buffers.count * sizeof(gss_buffer_desc));
++                GSSEAP_FREE(tokens->buffers.elements);
++            }
++            tokens->buffers.elements = newTokenBuffers;
++        }
++
++        tokens->types[tokens->buffers.count] = load_uint32_be(&p[0]);
++        tokenBuffer.length = load_uint32_be(&p[4]);
++
++        if (remain < 8 + tokenBuffer.length) {
++            major = GSS_S_DEFECTIVE_TOKEN;
++            *minor = GSSEAP_TOK_TRUNC;
++            goto cleanup;
++        }
++        tokenBuffer.value = &p[8];
++
++        tokens->buffers.elements[tokens->buffers.count] = tokenBuffer;
++        tokens->buffers.count++;
++
++        p      += 8 + tokenBuffer.length;
++        remain -= 8 + tokenBuffer.length;
++    } while (remain != 0);
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major))
++        gssEapReleaseInnerTokens(&tmpMinor, tokens, 0);
++
++    return major;
++}
++
++/*
++ * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $
++ */
++
++/* XXXX this code currently makes the assumption that a mech oid will
++   never be longer than 127 bytes.  This assumption is not inherent in
++   the interfaces, so the code can be fixed if the OSI namespace
++   balloons unexpectedly. */
++
++/*
++ * Each token looks like this:
++ * 0x60                 tag for APPLICATION 0, SEQUENCE
++ *                              (constructed, definite-length)
++ * <length>             possible multiple bytes, need to parse/generate
++ * 0x06                 tag for OBJECT IDENTIFIER
++ * <moid_length>        compile-time constant string (assume 1 byte)
++ * <moid_bytes>         compile-time constant string
++ * <inner_bytes>        the ANY containing the application token
++ * bytes 0,1 are the token type
++ * bytes 2,n are the token data
++ *
++ * Note that the token type field is a feature of RFC 1964 mechanisms and
++ * is not used by other GSSAPI mechanisms.  As such, a token type of -1
++ * is interpreted to mean that no token type should be expected or
++ * generated.
++ *
++ * For the purposes of this abstraction, the token "header" consists of
++ * the sequence tag and length octets, the mech OID DER encoding, and the
++ * first two inner bytes, which indicate the token type.  The token
++ * "body" consists of everything else.
++ */
++
++static size_t
++der_length_size(size_t length)
++{
++    if (length < (1<<7))
++        return 1;
++    else if (length < (1<<8))
++        return 2;
++#if INT_MAX == 0x7fff
++    else
++        return 3;
++#else
++    else if (length < (1<<16))
++        return 3;
++    else if (length < (1<<24))
++        return 4;
++    else
++        return 5;
++#endif
++}
++
++static void
++der_write_length(unsigned char **buf, size_t length)
++{
++    if (length < (1<<7)) {
++        *(*buf)++ = (unsigned char)length;
++    } else {
++        *(*buf)++ = (unsigned char)(der_length_size(length)+127);
++#if INT_MAX > 0x7fff
++        if (length >= (1<<24))
++            *(*buf)++ = (unsigned char)(length>>24);
++        if (length >= (1<<16))
++            *(*buf)++ = (unsigned char)((length>>16)&0xff);
++#endif
++        if (length >= (1<<8))
++            *(*buf)++ = (unsigned char)((length>>8)&0xff);
++        *(*buf)++ = (unsigned char)(length&0xff);
++    }
++}
++
++/* returns decoded length, or < 0 on failure.  Advances buf and
++   decrements bufsize */
++
++static int
++der_read_length(unsigned char **buf, ssize_t *bufsize)
++{
++    unsigned char sf;
++    int ret;
++
++    if (*bufsize < 1)
++        return -1;
++
++    sf = *(*buf)++;
++    (*bufsize)--;
++    if (sf & 0x80) {
++        if ((sf &= 0x7f) > ((*bufsize)-1))
++            return -1;
++        if (sf > sizeof(int))
++            return -1;
++        ret = 0;
++        for (; sf; sf--) {
++            ret = (ret<<8) + (*(*buf)++);
++            (*bufsize)--;
++        }
++    } else {
++        ret = sf;
++    }
++
++    return ret;
++}
++
++/* returns the length of a token, given the mech oid and the body size */
++
++size_t
++tokenSize(const gss_OID_desc *mech, size_t body_size)
++{
++    GSSEAP_ASSERT(mech != GSS_C_NO_OID);
++
++    /* set body_size to sequence contents size */
++    body_size += 4 + (size_t) mech->length;         /* NEED overflow check */
++    return 1 + der_length_size(body_size) + body_size;
++}
++
++/* fills in a buffer with the token header.  The buffer is assumed to
++   be the right size.  buf is advanced past the token header */
++
++void
++makeTokenHeader(
++    const gss_OID_desc *mech,
++    size_t body_size,
++    unsigned char **buf,
++    enum gss_eap_token_type tok_type)
++{
++    *(*buf)++ = 0x60;
++    der_write_length(buf, (tok_type == -1) ?2:4 + mech->length + body_size);
++    *(*buf)++ = 0x06;
++    *(*buf)++ = (unsigned char)mech->length;
++    memcpy(*buf, mech->elements, mech->length);
++    *buf += mech->length;
++    GSSEAP_ASSERT(tok_type != TOK_TYPE_NONE);
++    *(*buf)++ = (unsigned char)((tok_type>>8) & 0xff);
++    *(*buf)++ = (unsigned char)(tok_type & 0xff);
++}
++
++/*
++ * Given a buffer containing a token, reads and verifies the token,
++ * leaving buf advanced past the token header, and setting body_size
++ * to the number of remaining bytes.  Returns 0 on success,
++ * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
++ * mechanism in the token does not match the mech argument.  buf and
++ * *body_size are left unmodified on error.
++ */
++
++OM_uint32
++verifyTokenHeader(OM_uint32 *minor,
++                  gss_OID mech,
++                  size_t *body_size,
++                  unsigned char **buf_in,
++                  size_t toksize_in,
++                  enum gss_eap_token_type *ret_tok_type)
++{
++    unsigned char *buf = *buf_in;
++    ssize_t seqsize;
++    gss_OID_desc toid;
++    ssize_t toksize = (ssize_t)toksize_in;
++
++    *minor = GSSEAP_BAD_TOK_HEADER;
++
++    if (ret_tok_type != NULL)
++        *ret_tok_type = TOK_TYPE_NONE;
++
++    if ((toksize -= 1) < 0)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    if (*buf++ != 0x60)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    seqsize = der_read_length(&buf, &toksize);
++    if (seqsize < 0)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    if (seqsize != toksize)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    if ((toksize -= 1) < 0)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    if (*buf++ != 0x06)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    if ((toksize -= 1) < 0)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    toid.length = *buf++;
++
++    if ((toksize -= toid.length) < 0)
++        return GSS_S_DEFECTIVE_TOKEN;
++
++    toid.elements = buf;
++    buf += toid.length;
++
++    if (mech->elements == NULL) {
++        *mech = toid;
++        if (toid.length == 0)
++            return GSS_S_BAD_MECH;
++    } else if (!oidEqual(&toid, mech)) {
++        *minor = GSSEAP_WRONG_MECH;
++        return GSS_S_BAD_MECH;
++    }
++
++    if (ret_tok_type != NULL) {
++        if ((toksize -= 2) < 0)
++            return GSS_S_DEFECTIVE_TOKEN;
++
++        *ret_tok_type = load_uint16_be(buf);
++        buf += 2;
++    }
++
++    *buf_in = buf;
++    *body_size = toksize;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32
++gssEapAllocInnerTokens(OM_uint32 *minor,
++                       size_t count,
++                       struct gss_eap_token_buffer_set *tokens)
++{
++    OM_uint32 major;
++
++    tokens->buffers.count = 0;
++    tokens->buffers.elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
++    if (tokens->buffers.elements == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++
++    tokens->types = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
++    if (tokens->types == NULL) {
++        major = GSS_S_FAILURE;
++        *minor = ENOMEM;
++        goto cleanup;
++    }
++
++    major = GSS_S_COMPLETE;
++    *minor = 0;
++
++cleanup:
++    if (GSS_ERROR(major)) {
++        if (tokens->buffers.elements != NULL) {
++            GSSEAP_FREE(tokens->buffers.elements);
++            tokens->buffers.elements = NULL;
++        }
++        if (tokens->types != NULL) {
++            GSSEAP_FREE(tokens->types);
++            tokens->types = NULL;
++        }
++    }
++
++    return major;
++}
++
++OM_uint32
++gssEapReleaseInnerTokens(OM_uint32 *minor,
++                         struct gss_eap_token_buffer_set *tokens,
++                         int freeBuffers)
++{
++    OM_uint32 tmpMinor;
++    size_t i;
++
++    if (tokens->buffers.elements != NULL) {
++        if (freeBuffers) {
++            for (i = 0; i < tokens->buffers.count; i++)
++                gss_release_buffer(&tmpMinor, &tokens->buffers.elements[i]);
++        }
++        GSSEAP_FREE(tokens->buffers.elements);
++        tokens->buffers.elements = NULL;
++    }
++    tokens->buffers.count = 0;
++
++    if (tokens->types != NULL) {
++        GSSEAP_FREE(tokens->types);
++        tokens->types = NULL;
++    }
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
+diff --git a/mech_eap/verify_mic.c b/mech_eap/verify_mic.c
+new file mode 100644
+index 0000000..c0829f5
+--- /dev/null
++++ b/mech_eap/verify_mic.c
+@@ -0,0 +1,71 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Message protection services: verify a message integrity check.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_verify_mic(OM_uint32 *minor,
++               gss_ctx_id_t ctx,
++               gss_buffer_t message_buffer,
++               gss_buffer_t message_token,
++               gss_qop_t *qop_state)
++{
++    OM_uint32 major;
++    gss_iov_buffer_desc iov[3];
++    int conf_state;
++
++    if (message_token->length < 16) {
++        *minor = GSSEAP_TOK_TRUNC;
++        return GSS_S_BAD_SIG;
++    }
++
++    *minor = 0;
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
++    iov[0].buffer = *message_buffer;
++
++    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
++    iov[1].buffer = *message_token;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    major = gssEapUnwrapOrVerifyMIC(minor, ctx, &conf_state, qop_state,
++                                    iov, 2, TOK_TYPE_MIC);
++
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/wrap.c b/mech_eap/wrap.c
+new file mode 100644
+index 0000000..2e27fb3
+--- /dev/null
++++ b/mech_eap/wrap.c
+@@ -0,0 +1,137 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Message protection services: wrap.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_wrap(OM_uint32 *minor,
++         gss_ctx_id_t ctx,
++         int conf_req_flag,
++         gss_qop_t qop_req,
++         gss_buffer_t input_message_buffer,
++         int *conf_state,
++         gss_buffer_t output_message_buffer)
++{
++    OM_uint32 major;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    major = gssEapWrap(minor, ctx, conf_req_flag, qop_req,
++                       input_message_buffer,
++                       conf_state, output_message_buffer);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
++
++OM_uint32
++gssEapWrap(OM_uint32 *minor,
++           gss_ctx_id_t ctx,
++           int conf_req_flag,
++           gss_qop_t qop_req,
++           gss_buffer_t input_message_buffer,
++           int *conf_state,
++           gss_buffer_t output_message_buffer)
++{
++    OM_uint32 major, tmpMinor;
++    gss_iov_buffer_desc iov[4];
++    unsigned char *p;
++    int i;
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
++    iov[0].buffer.value = NULL;
++    iov[0].buffer.length = 0;
++
++    iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
++    iov[1].buffer = *input_message_buffer;
++
++    iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING;
++    iov[2].buffer.value = NULL;
++    iov[2].buffer.length = 0;
++
++    iov[3].type = GSS_IOV_BUFFER_TYPE_TRAILER;
++    iov[3].buffer.value = NULL;
++    iov[3].buffer.length = 0;
++
++    major = gssEapWrapIovLength(minor, ctx, conf_req_flag, qop_req,
++                                NULL, iov, 4);
++    if (GSS_ERROR(major)) {
++        return major;
++    }
++
++    for (i = 0, output_message_buffer->length = 0; i < 4; i++) {
++        output_message_buffer->length += iov[i].buffer.length;
++    }
++
++    output_message_buffer->value = GSSEAP_MALLOC(output_message_buffer->length);
++    if (output_message_buffer->value == NULL) {
++        *minor = ENOMEM;
++        return GSS_S_FAILURE;
++    }
++
++    for (i = 0, p = output_message_buffer->value; i < 4; i++) {
++        if (iov[i].type == GSS_IOV_BUFFER_TYPE_DATA) {
++            memcpy(p, input_message_buffer->value, input_message_buffer->length);
++        }
++        iov[i].buffer.value = p;
++        p += iov[i].buffer.length;
++    }
++
++    major = gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state,
++                               iov, 4, TOK_TYPE_WRAP);
++    if (GSS_ERROR(major)) {
++        gss_release_buffer(&tmpMinor, output_message_buffer);
++    }
++
++    return major;
++}
+diff --git a/mech_eap/wrap_iov.c b/mech_eap/wrap_iov.c
+new file mode 100644
+index 0000000..be890b6
+--- /dev/null
++++ b/mech_eap/wrap_iov.c
+@@ -0,0 +1,379 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 2008 by the Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ */
++
++/*
++ * Message protection services: wrap with scatter-gather API.
++ */
++
++#include "gssapiP_eap.h"
++
++unsigned char
++rfc4121Flags(gss_ctx_id_t ctx, int receiving)
++{
++    unsigned char flags;
++    int isAcceptor;
++
++    isAcceptor = !CTX_IS_INITIATOR(ctx);
++    if (receiving)
++        isAcceptor = !isAcceptor;
++
++    flags = 0;
++    if (isAcceptor)
++        flags |= TOK_FLAG_SENDER_IS_ACCEPTOR;
++
++    if ((ctx->flags & CTX_FLAG_KRB_REAUTH) &&
++        (ctx->gssFlags & GSS_C_MUTUAL_FLAG))
++        flags |= TOK_FLAG_ACCEPTOR_SUBKEY;
++
++    return flags;
++}
++
++OM_uint32
++gssEapWrapOrGetMIC(OM_uint32 *minor,
++                   gss_ctx_id_t ctx,
++                   int conf_req_flag,
++                   int *conf_state,
++                   gss_iov_buffer_desc *iov,
++                   int iov_count,
++                   enum gss_eap_token_type toktype)
++{
++    krb5_error_code code = 0;
++    gss_iov_buffer_t header;
++    gss_iov_buffer_t padding;
++    gss_iov_buffer_t trailer;
++    unsigned char flags;
++    unsigned char *outbuf = NULL;
++    unsigned char *tbuf = NULL;
++    int keyUsage;
++    size_t rrc = 0;
++    size_t gssHeaderLen, gssTrailerLen;
++    size_t dataLen, assocDataLen;
++    krb5_context krbContext;
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_crypto krbCrypto = NULL;
++#endif
++
++    if (ctx->encryptionType == ENCTYPE_NULL) {
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    flags = rfc4121Flags(ctx, FALSE);
++
++    if (toktype == TOK_TYPE_WRAP) {
++        keyUsage = CTX_IS_INITIATOR(ctx)
++                   ? KEY_USAGE_INITIATOR_SEAL
++                   : KEY_USAGE_ACCEPTOR_SEAL;
++    } else {
++        keyUsage = CTX_IS_INITIATOR(ctx)
++                   ? KEY_USAGE_INITIATOR_SIGN
++                   : KEY_USAGE_ACCEPTOR_SIGN;
++    }
++
++    gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
++
++    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
++    if (header == NULL) {
++        *minor = GSSEAP_MISSING_IOV;
++        return GSS_S_FAILURE;
++    }
++
++    padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
++    if (padding != NULL)
++        padding->buffer.length = 0;
++
++    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
++    if (code != 0)
++        goto cleanup;
++#endif
++
++    if (toktype == TOK_TYPE_WRAP && conf_req_flag) {
++        size_t krbHeaderLen, krbTrailerLen, krbPadLen;
++        size_t ec = 0, confDataLen = dataLen - assocDataLen;
++
++        code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                               KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
++        if (code != 0)
++            goto cleanup;
++
++        code = krbPaddingLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                                confDataLen + 16 /* E(Header) */,
++                                &krbPadLen);
++        if (code != 0)
++            goto cleanup;
++
++        if (krbPadLen == 0 && (ctx->gssFlags & GSS_C_DCE_STYLE)) {
++            /* Windows rejects AEAD tokens with non-zero EC */
++            code = krbBlockSize(krbContext, KRB_CRYPTO_CONTEXT(ctx), &ec);
++            if (code != 0)
++                goto cleanup;
++        } else
++            ec = krbPadLen;
++
++        code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                               KRB5_CRYPTO_TYPE_TRAILER, &krbTrailerLen);
++        if (code != 0)
++            goto cleanup;
++
++        gssHeaderLen = 16 /* Header */ + krbHeaderLen;
++        gssTrailerLen = ec + 16 /* E(Header) */ + krbTrailerLen;
++
++        if (trailer == NULL) {
++            rrc = gssTrailerLen;
++            /* Workaround for Windows bug where it rotates by EC + RRC */
++            if (ctx->gssFlags & GSS_C_DCE_STYLE)
++                rrc -= ec;
++            gssHeaderLen += gssTrailerLen;
++        }
++
++        if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
++            code = gssEapAllocIov(header, (size_t)gssHeaderLen);
++        } else if (header->buffer.length < gssHeaderLen)
++            code = GSSEAP_WRONG_SIZE;
++        if (code != 0)
++            goto cleanup;
++        outbuf = (unsigned char *)header->buffer.value;
++        header->buffer.length = (size_t)gssHeaderLen;
++
++        if (trailer != NULL) {
++            if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
++                code = gssEapAllocIov(trailer, (size_t)gssTrailerLen);
++            else if (trailer->buffer.length < gssTrailerLen)
++                code = GSSEAP_WRONG_SIZE;
++            if (code != 0)
++                goto cleanup;
++            trailer->buffer.length = (size_t)gssTrailerLen;
++        }
++
++        /* TOK_ID */
++        store_uint16_be((uint16_t)toktype, outbuf);
++        /* flags */
++        outbuf[2] = flags
++                     | (conf_req_flag ? TOK_FLAG_WRAP_CONFIDENTIAL : 0);
++        /* filler */
++        outbuf[3] = 0xFF;
++        /* EC */
++        store_uint16_be(ec, outbuf + 4);
++        /* RRC */
++        store_uint16_be(0, outbuf + 6);
++        store_uint64_be(ctx->sendSeq, outbuf + 8);
++
++        /*
++         * EC | copy of header to be encrypted, located in
++         * (possibly rotated) trailer
++         */
++        if (trailer == NULL)
++            tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
++        else
++            tbuf = (unsigned char *)trailer->buffer.value;
++
++        memset(tbuf, 0xFF, ec);
++        memcpy(tbuf + ec, header->buffer.value, 16);
++
++        code = gssEapEncrypt(krbContext,
++                             ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
++                             ec, rrc, KRB_CRYPTO_CONTEXT(ctx),
++                             keyUsage, iov, iov_count);
++        if (code != 0)
++            goto cleanup;
++
++        /* RRC */
++        store_uint16_be(rrc, outbuf + 6);
++
++        ctx->sendSeq++;
++    } else if (toktype == TOK_TYPE_WRAP && !conf_req_flag) {
++    wrap_with_checksum:
++
++        gssHeaderLen = 16;
++
++        code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                               KRB5_CRYPTO_TYPE_CHECKSUM, &gssTrailerLen);
++        if (code != 0)
++            goto cleanup;
++
++        GSSEAP_ASSERT(gssTrailerLen <= 0xFFFF);
++
++        if (trailer == NULL) {
++            rrc = gssTrailerLen;
++            gssHeaderLen += gssTrailerLen;
++        }
++
++        if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
++            code = gssEapAllocIov(header, (size_t)gssHeaderLen);
++        else if (header->buffer.length < gssHeaderLen)
++            code = GSSEAP_WRONG_SIZE;
++        if (code != 0)
++            goto cleanup;
++        outbuf = (unsigned char *)header->buffer.value;
++        header->buffer.length = (size_t)gssHeaderLen;
++
++        if (trailer != NULL) {
++            if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
++                code = gssEapAllocIov(trailer, (size_t)gssTrailerLen);
++            else if (trailer->buffer.length < gssTrailerLen)
++                code = GSSEAP_WRONG_SIZE;
++            if (code != 0)
++                goto cleanup;
++            trailer->buffer.length = (size_t)gssTrailerLen;
++        }
++
++        /* TOK_ID */
++        store_uint16_be((uint16_t)toktype, outbuf);
++        /* flags */
++        outbuf[2] = flags;
++        /* filler */
++        outbuf[3] = 0xFF;
++        if (toktype == TOK_TYPE_WRAP) {
++            /* Use 0 for checksum calculation, substitute
++             * checksum length later.
++             */
++            /* EC */
++            store_uint16_be(0, outbuf + 4);
++            /* RRC */
++            store_uint16_be(0, outbuf + 6);
++        } else {
++            /* MIC and DEL store 0xFF in EC and RRC */
++            store_uint16_be(0xFFFF, outbuf + 4);
++            store_uint16_be(0xFFFF, outbuf + 6);
++        }
++        store_uint64_be(ctx->sendSeq, outbuf + 8);
++
++        code = gssEapSign(krbContext, ctx->checksumType, rrc,
++                          KRB_CRYPTO_CONTEXT(ctx), keyUsage,
++                          iov, iov_count);
++        if (code != 0)
++            goto cleanup;
++
++        ctx->sendSeq++;
++
++        if (toktype == TOK_TYPE_WRAP) {
++            /* Fix up EC field */
++            store_uint16_be(gssTrailerLen, outbuf + 4);
++            /* Fix up RRC field */
++            store_uint16_be(rrc, outbuf + 6);
++        }
++    } else if (toktype == TOK_TYPE_MIC) {
++        trailer = NULL;
++        goto wrap_with_checksum;
++    } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
++        trailer = NULL;
++        goto wrap_with_checksum;
++    } else {
++        abort();
++    }
++
++    code = 0;
++    if (conf_state != NULL)
++        *conf_state = conf_req_flag;
++
++cleanup:
++    if (code != 0)
++        gssEapReleaseIov(iov, iov_count);
++#ifdef HAVE_HEIMDAL_VERSION
++    if (krbCrypto != NULL)
++        krb5_crypto_destroy(krbContext, krbCrypto);
++#endif
++
++    *minor = code;
++
++    return (code == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_wrap_iov(OM_uint32 *minor,
++             gss_ctx_id_t ctx,
++             int conf_req_flag,
++             gss_qop_t qop_req,
++             int *conf_state,
++             gss_iov_buffer_desc *iov,
++             int iov_count)
++{
++    OM_uint32 major;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    if (qop_req != GSS_C_QOP_DEFAULT) {
++        *minor = GSSEAP_UNKNOWN_QOP;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    major = gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state,
++                               iov, iov_count, TOK_TYPE_WRAP);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/wrap_iov_length.c b/mech_eap/wrap_iov_length.c
+new file mode 100644
+index 0000000..247b78d
+--- /dev/null
++++ b/mech_eap/wrap_iov_length.c
+@@ -0,0 +1,234 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++/*
++ * Copyright 2008 by the Massachusetts Institute of Technology.
++ * All Rights Reserved.
++ *
++ * Export of this software from the United States of America may
++ *   require a specific license from the United States Government.
++ *   It is the responsibility of any person or organization contemplating
++ *   export to obtain such a license before exporting.
++ *
++ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
++ * distribute this software and its documentation for any purpose and
++ * without fee is hereby granted, provided that the above copyright
++ * notice appear in all copies and that both that copyright notice and
++ * this permission notice appear in supporting documentation, and that
++ * the name of M.I.T. not be used in advertising or publicity pertaining
++ * to distribution of the software without specific, written prior
++ * permission.  Furthermore if you modify this software you must label
++ * your software as modified software and not distribute it in such a
++ * fashion that it might be confused with the original M.I.T. software.
++ * M.I.T. makes no representations about the suitability of
++ * this software for any purpose.  It is provided "as is" without express
++ * or implied warranty.
++ */
++
++/*
++ * Message protection services: determine protected message size.
++ */
++
++#include "gssapiP_eap.h"
++
++#define INIT_IOV_DATA(_iov)     do { (_iov)->buffer.value = NULL;       \
++        (_iov)->buffer.length = 0; }                                    \
++    while (0)
++
++OM_uint32
++gssEapWrapIovLength(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    int conf_req_flag,
++                    gss_qop_t qop_req,
++                    int *conf_state,
++                    gss_iov_buffer_desc *iov,
++                    int iov_count)
++{
++    gss_iov_buffer_t header, trailer, padding;
++    size_t dataLength, assocDataLength;
++    size_t gssHeaderLen, gssPadLen, gssTrailerLen;
++    size_t krbHeaderLen = 0, krbTrailerLen = 0, krbPadLen = 0;
++    krb5_error_code code;
++    krb5_context krbContext;
++    int dce_style;
++    size_t ec;
++#ifdef HAVE_HEIMDAL_VERSION
++    krb5_crypto krbCrypto = NULL;
++#endif
++
++    if (qop_req != GSS_C_QOP_DEFAULT) {
++        *minor = GSSEAP_UNKNOWN_QOP;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    if (ctx->encryptionType == ENCTYPE_NULL) {
++        *minor = GSSEAP_KEY_UNAVAILABLE;
++        return GSS_S_UNAVAILABLE;
++    }
++
++    GSSEAP_KRB_INIT(&krbContext);
++
++    header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
++    if (header == NULL) {
++        *minor = GSSEAP_MISSING_IOV;
++        return GSS_S_FAILURE;
++    }
++    INIT_IOV_DATA(header);
++
++    trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
++    if (trailer != NULL) {
++        INIT_IOV_DATA(trailer);
++    }
++
++    dce_style = ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0);
++
++    /* For CFX, EC is used instead of padding, and is placed in header or trailer */
++    padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
++    if (padding != NULL) {
++        INIT_IOV_DATA(padding);
++    }
++
++    gssEapIovMessageLength(iov, iov_count, &dataLength, &assocDataLength);
++
++    if (conf_req_flag && gssEapIsIntegrityOnly(iov, iov_count))
++        conf_req_flag = FALSE;
++
++    gssHeaderLen = gssPadLen = gssTrailerLen = 0;
++
++#ifdef HAVE_HEIMDAL_VERSION
++    code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
++    if (code != 0)
++        return code;
++#endif
++
++    code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                           conf_req_flag ?
++                                KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
++                           &krbTrailerLen);
++    if (code != 0) {
++        *minor = code;
++        return GSS_S_FAILURE;
++    }
++
++    if (conf_req_flag) {
++        code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                               KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
++        if (code != 0) {
++            *minor = code;
++            return GSS_S_FAILURE;
++        }
++    }
++
++    gssHeaderLen = 16; /* Header */
++    if (conf_req_flag) {
++        gssHeaderLen += krbHeaderLen; /* Kerb-Header */
++        gssTrailerLen = 16 /* E(Header) */ + krbTrailerLen; /* Kerb-Trailer */
++
++        code = krbPaddingLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
++                                dataLength - assocDataLength + 16 /* E(Header) */,
++                                &krbPadLen);
++        if (code != 0) {
++            *minor = code;
++            return GSS_S_FAILURE;
++        }
++
++        if (krbPadLen == 0 && dce_style) {
++            /* Windows rejects AEAD tokens with non-zero EC */
++            code = krbBlockSize(krbContext, KRB_CRYPTO_CONTEXT(ctx), &ec);
++            if (code != 0) {
++                *minor = code;
++                return GSS_S_FAILURE;
++            }
++        } else
++            ec = krbPadLen;
++
++        gssTrailerLen += ec;
++    } else {
++        gssTrailerLen = krbTrailerLen; /* Kerb-Checksum */
++    }
++
++    dataLength += gssPadLen;
++
++    if (trailer == NULL)
++        gssHeaderLen += gssTrailerLen;
++    else
++        trailer->buffer.length = gssTrailerLen;
++
++    GSSEAP_ASSERT(gssPadLen == 0 || padding != NULL);
++
++    if (padding != NULL)
++        padding->buffer.length = gssPadLen;
++
++    header->buffer.length = gssHeaderLen;
++
++    if (conf_state != NULL)
++        *conf_state = conf_req_flag;
++
++    *minor = 0;
++    return GSS_S_COMPLETE;
++}
++
++OM_uint32 GSSAPI_CALLCONV
++gss_wrap_iov_length(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    int conf_req_flag,
++                    gss_qop_t qop_req,
++                    int *conf_state,
++                    gss_iov_buffer_desc *iov,
++                    int iov_count)
++{
++    OM_uint32 major;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    major = gssEapWrapIovLength(minor, ctx, conf_req_flag, qop_req,
++                                conf_state, iov, iov_count);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+diff --git a/mech_eap/wrap_size_limit.c b/mech_eap/wrap_size_limit.c
+new file mode 100644
+index 0000000..d11fd63
+--- /dev/null
++++ b/mech_eap/wrap_size_limit.c
+@@ -0,0 +1,97 @@
++/*
++ * Copyright (c) 2011, JANET(UK)
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ *
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ *
++ * 3. Neither the name of JANET(UK) nor the names of its contributors
++ *    may be used to endorse or promote products derived from this software
++ *    without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ */
++
++/*
++ * Message protection services: determine maximum input size.
++ */
++
++#include "gssapiP_eap.h"
++
++OM_uint32 GSSAPI_CALLCONV
++gss_wrap_size_limit(OM_uint32 *minor,
++                    gss_ctx_id_t ctx,
++                    int conf_req_flag,
++                    gss_qop_t qop_req,
++                    OM_uint32 req_output_size,
++                    OM_uint32 *max_input_size)
++{
++    gss_iov_buffer_desc iov[4];
++    OM_uint32 major, overhead;
++
++    if (ctx == GSS_C_NO_CONTEXT) {
++        *minor = EINVAL;
++        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
++    }
++
++    *minor = 0;
++
++    GSSEAP_MUTEX_LOCK(&ctx->mutex);
++
++    if (!CTX_IS_ESTABLISHED(ctx)) {
++        major = GSS_S_NO_CONTEXT;
++        *minor = GSSEAP_CONTEXT_INCOMPLETE;
++        goto cleanup;
++    }
++
++    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
++    iov[0].buffer.value = NULL;
++    iov[0].buffer.length = 0;
++
++    iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
++    iov[1].buffer.length = req_output_size;
++    iov[1].buffer.value = NULL;
++
++    iov[2].type = GSS_IOV_BUFFER_TYPE_PADDING;
++    iov[2].buffer.value = NULL;
++    iov[2].buffer.length = 0;
++
++    iov[3].type = GSS_IOV_BUFFER_TYPE_TRAILER;
++    iov[3].buffer.value = NULL;
++    iov[3].buffer.length = 0;
++
++    major = gssEapWrapIovLength(minor, ctx, conf_req_flag, qop_req,
++                                NULL, iov, 4);
++    if (GSS_ERROR(major))
++        goto cleanup;
++
++    overhead = iov[0].buffer.length + iov[3].buffer.length;
++
++    if (iov[2].buffer.length == 0 && overhead < req_output_size)
++        *max_input_size = req_output_size - overhead;
++    else
++        *max_input_size = 0;
++
++cleanup:
++    GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
++
++    return major;
++}
+-- 
+1.7.5.4
+
index 5ab9637..b2650a5 100644 (file)
@@ -2,22 +2,33 @@
 <!DOCTYPE moduleset SYSTEM "moduleset.dtd">
 <?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
 <moduleset>
 <!DOCTYPE moduleset SYSTEM "moduleset.dtd">
 <?xml-stylesheet type="text/xsl" href="moduleset.xsl"?>
 <moduleset>
+    
+    <repository type="git" name="moonshot.janet.git"
+    href="http://www.project-moonshot.org/git"/>
 
     <repository type="git" name="moonshot-ui.janet.git"
     href="http://www.project-moonshot.org/git/moonshot-ui.git"/>
 
     <repository type="git" name="moonshot-ui.janet.git"
     href="http://www.project-moonshot.org/git/moonshot-ui.git"/>
-
+    
     <repository type="git" name="moonshot-ui.gitorious.git"
     <repository type="git" name="moonshot-ui.gitorious.git"
-    href="git://gitorious.codethink.co.uk/moonshot-ui/moonshot-ui.git"/>
-
-    <include href="gtk-osx.modules"/>
+    href="git@gitorious.codethink.co.uk:moonshot-ui/moonshot-ui.git"/>
+    
+    <repository type="git" name="cyrus-sasl.janet.git"
+    href="http://www.project-moonshot.org/git/cyrus-sasl"/>
+    
+    <repository type="tarball" name="ftp.cyrus-sasl.org" default="yes"
+    href="ftp://ftp.cyrusimap.org/cyrus-sasl/"/>
 
 
+<!--    <include href="gtk-osx.modules"/> -->
+  <include href="http://git.gnome.org/browse/gtk-osx/plain/modulesets-stable/gtk-osx.modules"/>
     <metamodule id="meta-moonshot-mac-client">
         <dependencies>
             <dep package="moonshot-ui"/>
     <metamodule id="meta-moonshot-mac-client">
         <dependencies>
             <dep package="moonshot-ui"/>
+            <dep package="cyrus-sasl"/> 
+<!--            <dep package="moonshot"/> -->
         </dependencies>
     </metamodule>
 
         </dependencies>
     </metamodule>
 
-    <autotools id="moonshot-ui">
+    <autotools id="moonshot-ui" makeinstallargs="install DESTDIR=$HOME/moonshot/mac-client-installer/moonshot-ui" >
         <branch 
         repo="moonshot-ui.gitorious.git"/>
         <dependencies>
         <branch 
         repo="moonshot-ui.gitorious.git"/>
         <dependencies>
             <dep package="pango"/>
             <dep package="atk"/>
             <dep package="gdk-pixbuf"/>
             <dep package="pango"/>
             <dep package="atk"/>
             <dep package="gdk-pixbuf"/>
-            <dep package="gtk+"/>
-            <dep package="gtk-mac-integration"/>
+            <dep package="gtk+"/> 
+            <dep package="vala"/>
+            <dep package="shared-mime-info"/>
+            <dep package="meta-gtk-osx-themes"/>
             <dep package="meta-gtk-osx-core"/>
             <dep package="meta-gtk-osx-core"/>
+<!--            <dep package="gtk+-3.0"/> -->
+            <dep package="gtk-mac-integration"/>
         </dependencies>
     </autotools>
     
         </dependencies>
     </autotools>
     
             <dep package="glib"/>
         </dependencies>
     </tarball>
             <dep package="glib"/>
         </dependencies>
     </tarball>
-    
 
 
+    
+    <autotools id="moonshot" autogenargs="--enable-acceptor=no --with-krb5=$PREFIX/usr/local"
+        makeinstallargs="install DESTDIR=$HOME/moonshot/mac-client-installer/moonshot">
+        <branch 
+        repo="moonshot.janet.git" module="moonshot.git">
+        <patch file="0001-Move-moonshot-files-up.patch" strip="1"/>
+        </branch>
+        <dependencies>
+       </dependencies>
+    </autotools>
+    
+    <autotools id="cyrus-sasl" autogenargs="--with-gss_impl=mit"
+        makeinstallargs="install DESTDIR=$HOME/moonshot/mac-client-installer/sasl">
+        <branch 
+        repo="cyrus-sasl.janet.git"/>
+        <dependencies>
+        </dependencies>
+    </autotools>
 
 </moduleset>
 
 
 </moduleset>