--- /dev/null
+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
+