From: Pete Fotheringham Date: Thu, 29 Dec 2011 09:34:12 +0000 (+0000) Subject: Checks out moonshot code correctly: Builds with the following X-Git-Tag: tr-beta1~46^2~13 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=moonshot.git;a=commitdiff_plain;h=dbfec61355edcbbb897f8d8089d7f5ef2ab0c013 Checks out moonshot code correctly: Builds with the following $ jhbuild build moonshot [4] Start shell*** Error during phase configure of moonshot: $ git apply ~/moonshot/mac-client-installer/0001-Move-moonshot-files-up.patch $ cp -R moonshot/libeap . $ ./autogen.sh --prefix /Users/pete/gtk/inst --libdir '/Users/pete/gtk/inst/lib' --enable-acceptor=no --with-krb5=$PREFIX/usr/local $ ./configure --enable-acceptor=no --with-krb5=$PREFIX/usr/local $ exit [2] Ignore error and continue to build --- diff --git a/mac-client-installer/0001-Move-moonshot-files-up.patch b/mac-client-installer/0001-Move-moonshot-files-up.patch new file mode 100644 index 0000000..b1d898a --- /dev/null +++ b/mac-client-installer/0001-Move-moonshot-files-up.patch @@ -0,0 +1,25641 @@ +From df22702d72d30843193d98589fee090f2fe63201 Mon Sep 17 00:00:00 2001 +From: Pete Fotheringham +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 . ++# ++# 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 or send patches to ++# . ++ ++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 . ++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 - @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 ++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 = ', and ++appropriately ( 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 @ -pass host@ \ ++ "Testing GSS EAP" ++% gss-server -port 5555 -export host@ ++ ++Note: for SASL you will be prompted for a username and password. ++ ++% client -C -p 5556 -s host -m EAP-AES128 ++% server -c -p 5556 -s host -h ++ ++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: ++ ++ ++ ++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 ++ ++/* ++ * 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 ++#include ++#include ++#ifdef HAVE_UNISTD_H ++#include ++#endif ++#ifdef HAVE_STDLIB_H ++#include ++#endif ++#ifdef HAVE_STDARG_H ++#include ++#endif ++#include ++#ifdef HAVE_SYS_PARAM_H ++#include ++#endif ++ ++#ifdef WIN32 ++#ifndef MAXHOSTNAMELEN ++# include ++# define MAXHOSTNAMELEN NI_MAXHOST ++#endif ++#endif ++ ++/* GSS headers */ ++#include ++#include ++#ifdef HAVE_HEIMDAL_VERSION ++typedef struct gss_any *gss_any_t; ++#else ++#include ++#endif ++#include "gssapi_eap.h" ++ ++#ifndef HAVE_GSS_INQUIRE_ATTRS_FOR_MECH ++typedef const gss_OID_desc *gss_const_OID; ++#endif ++ ++/* Kerberos headers */ ++#include ++ ++/* EAP headers */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef GSSEAP_ENABLE_ACCEPTOR ++/* FreeRADIUS headers */ ++#ifdef __cplusplus ++extern "C" { ++#ifndef WIN32 ++#define operator fr_operator ++#endif ++#endif ++#include ++#include ++ ++#undef pid_t ++ ++/* libradsec headers */ ++#include ++#include ++#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 ++ ++#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 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 ++#endif ++#ifdef HAVE_STDINT_H ++#include ++#endif ++#include ++#include ++ ++#include ++ ++#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 ++#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 ++ ++#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 ++ ++#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 ++#include ++#include ++#include ++#include ++ ++/* 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 ++#include ++ ++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 /* may need to use ShFolder.h instead */ ++# include ++#else ++# include ++#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 ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++ ++#ifdef HAVE_SHIBRESOLVER ++#include ++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 ++ ++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(ctx); ++ ++ if (radius->m_vps != NULL) ++ m_vps = copyAvps(const_cast(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 &attrs, VALUE_PAIR *vp) ++{ ++ for (std::vector::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 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 ++ ++/* ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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(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 ++ (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(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(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 ++ (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 &statements = ++ const_cast(assertion)->getAttributeStatements(); ++ ++ for (vector::const_iterator s = statements.begin(); ++ s != statements.end(); ++ ++s) { ++ const vector &attrs = ++ const_cast(*s)->getAttributes(); ++ ++ for (vector::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 * ++decomposeAttributeName(const gss_buffer_t attr) ++{ ++ BaseRefVectorOf *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 *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 *components = decomposeAttributeName(attr); ++ if (components == NULL) ++ return false; ++ ++ /* For each attribute statement, look for an attribute match */ ++ const vector &statements = ++ const_cast(assertion)->getAttributeStatements(); ++ ++ for (vector::const_iterator s = statements.begin(); ++ s != statements.end(); ++ ++s) { ++ const vector &attrs = ++ const_cast(*s)->getAttributes(); ++ ssize_t index = -1, i = 0; ++ ++ /* There's got to be an easier way to do this */ ++ for (vector::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 *components = decomposeAttributeName(attr); ++ if (components == NULL) ++ return false; ++ ++ /* For each attribute statement, look for an attribute match */ ++ const vector &statements = ++ const_cast(assertion)->getAttributeStatements(); ++ const saml2::Attribute *ret = NULL; ++ ++ for (vector::const_iterator s = statements.begin(); ++ s != statements.end(); ++ ++s) { ++ const vector &attrs = ++ const_cast(*s)->getAttributes(); ++ ++ for (vector::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(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 ++#ifndef HAVE_OPENSAML ++#include ++#include ++#endif ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++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()) ++ ; ++} ++ ++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(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 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 ++ (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 ++ (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::const_iterator a = m_attributes.begin(); ++ a != m_attributes.end(); ++ ++a) ++ { ++ for (vector::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 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::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::const_iterator a = m_attributes.begin(); ++ a != m_attributes.end(); ++ ++a) ++ { ++ for (vector::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(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(shibAttr); ++ const ScopedAttribute *scopedAttr = ++ dynamic_cast(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 v = duplicateAttributes(m_attributes); ++ ++ output = (gss_any_t)new vector (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 *v = ((vector *)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::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 ++gss_eap_shib_attr_provider::duplicateAttributes(const vector src) ++{ ++ vector dst; ++ ++ for (vector::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 ++ ++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 getAttributes(void) const { ++ return m_attributes; ++ } ++ ++private: ++ static shibsp::Attribute * ++ duplicateAttribute(const shibsp::Attribute *src); ++ static std::vector ++ duplicateAttributes(const std::vector 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 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) ++ * possible multiple bytes, need to parse/generate ++ * 0x06 tag for OBJECT IDENTIFIER ++ * compile-time constant string (assume 1 byte) ++ * compile-time constant string ++ * 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 + diff --git a/mac-client-installer/moonshot-mac.modules b/mac-client-installer/moonshot-mac.modules index 5ab9637..b2650a5 100644 --- a/mac-client-installer/moonshot-mac.modules +++ b/mac-client-installer/moonshot-mac.modules @@ -2,22 +2,33 @@ + + - + - - + href="git@gitorious.codethink.co.uk:moonshot-ui/moonshot-ui.git"/> + + + + + + + + - + @@ -31,9 +42,13 @@ - - + + + + + + @@ -45,8 +60,25 @@ - + + + + + + + + + + + + + +