From 5e863ddff6ef99a59acfb3c41f0b86ebc1468be7 Mon Sep 17 00:00:00 2001 From: kouril Date: Wed, 15 Dec 2010 13:25:05 +0000 Subject: [PATCH] importing current version of mod_auth_gssapi --- LICENSE | 34 +----- Makefile.in | 30 ++--- apxs.sh | 5 +- config.h.in | 13 --- configure.in | 327 ++++++++++++------------------------------------------ gss.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mod_auth_gssapi.c | 189 +++++++++++++++++++++++++++++++ mod_auth_gssapi.h | 46 ++++++++ 8 files changed, 646 insertions(+), 320 deletions(-) create mode 100644 gss.c create mode 100644 mod_auth_gssapi.c create mode 100644 mod_auth_gssapi.h diff --git a/LICENSE b/LICENSE index 7812b5a..8c3ba52 100644 --- a/LICENSE +++ b/LICENSE @@ -1,36 +1,6 @@ -mod_auth_kerb license +mod_auth_gssapi license --------------------- -/* - * Copyright (c) 2004-2006 Masarykova universita - * (Masaryk University, Brno, Czech Republic) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, 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 University nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ +XXXX This package also contains parts (see the spnego subdirectory) based on the diff --git a/Makefile.in b/Makefile.in index 36f6046..e9005d8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,26 +1,23 @@ APXS = @APXS@ -KRB5_CPPFLAGS = @KRB5_CPPFLAGS@ -KRB5_LDFLAGS = @KRB5_LDFLAGS@ -KRB4_CPPFLAGS = @KRB4_CPPFLAGS@ -KRB4_LDFLAGS = @KRB4_LDFLAGS@ -LIB_resolv = @LIB_resolv@ +GSS_CPPFLAGS = @GSS_CPPFLAGS@ +GSS_LDFLAGS = @GSS_LDFLAGS@ SPNEGO_SRCS = @SPNEGO_SRCS@ -CPPFLAGS = -I. -Ispnegokrb5 $(KRB5_CPPFLAGS) $(KRB4_CPPFLAGS) $(DEFS) -LDFLAGS = $(KRB5_LDFLAGS) $(KRB4_LDFLAGS) $(LIB_resolv) +CPPFLAGS = -I. -Ispnegokrb5 $(GSS_CPPFLAGS) $(DEFS) +LDFLAGS = $(GSS_LDFLAGS) CFLAGS = -all: src/mod_auth_kerb.so +all: mod_auth_gssapi.la -src/mod_auth_kerb.so: src/mod_auth_kerb.c $(SPNEGO_SRCS) - ./apxs.sh "${CPPFLAGS}" "${LDFLAGS}" "${SPNEGO_SRCS}" "${APXS}" "-c" "src/mod_auth_kerb.c" +mod_auth_gssapi.la: mod_auth_gssapi.c $(SPNEGO_SRCS) + ./apxs.sh "${CPPFLAGS}" "${LDFLAGS}" "${SPNEGO_SRCS}" "${APXS} -c" install: - ./apxs.sh "${CPPFLAGS}" "${LDFLAGS}" "${SPNEGO_SRCS}" "${APXS}" "-c -i" "src/mod_auth_kerb.c" + ./apxs.sh "${CPPFLAGS}" "${LDFLAGS}" "${SPNEGO_SRCS}" "${APXS} -c -i -n auth_gssapi" clean: - for i in . src spnegokrb5; do \ - $(RM) $$i/*.{o,so,a,la,lo,slo} core; \ + for i in . spnegokrb5; do \ + (cd $$i && $(RM) *.o *.so *.a *.la *.lo *.slo); \ $(RM) -rf $$i/.libs; \ done @@ -28,11 +25,4 @@ distclean: clean $(RM) config.h config.status Makefile config.log $(RM) -rf autom4te.cache -make_release: - echo "Did you increase version numbers?" - autoconf - $(RM) -rf autom4te.cache - $(RM) -rf .cvsignore spnegokrb5/.cvsignore src/.cvsignore - $(RM) -rf CVS spnegokrb5/CVS src/CVS - .PHONY: all install clean distclean diff --git a/apxs.sh b/apxs.sh index d8a776a..9dd608f 100755 --- a/apxs.sh +++ b/apxs.sh @@ -1,5 +1,8 @@ #!/bin/sh +# +# XXX cleanup desperately needed +# cppflags=`[ -n "$1" ] && echo $1 | sed -e 's/\([^ ]\+\)/-Wc,\1/g'` ldflags=`[ -n "$2" ] && echo $2 | sed -e 's/\([^ ]\+\)/-Wl,\1/g'` -ret=eval "$4" $5 $cppflags $ldflags src/mod_auth_kerb.c $3 +ret=eval $4 $cppflags $ldflags mod_auth_gssapi.c $3 exit $ret diff --git a/config.h.in b/config.h.in index ca90d99..def453e 100644 --- a/config.h.in +++ b/config.h.in @@ -1,4 +1,3 @@ - /* Define to the version of this package. */ /* Conflicts with defintions from Apache */ /* #undef PACKAGE_VERSION */ @@ -6,21 +5,9 @@ /* Define to `unsigned' if does not define. */ #undef size_t -/* Define if you are using the Heimdal implementation of Krb5 */ -#undef HEIMDAL - -/* Define if you want to enable support for Kerberos5 */ -#undef KRB5 - -/* Define if you want to enable support for Kerberos4 */ -#undef KRB4 - /* Define if your GSSAPI library supports handling SPNEGO tokens */ #undef GSSAPI_SUPPORTS_SPNEGO -/* Define if your krb supports krb5_cc_new_unique function to deal with threading issues */ -#undef HAVE_KRB5_CC_NEW_UNIQUE - /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H diff --git a/configure.in b/configure.in index b00c43c..a189bf8 100644 --- a/configure.in +++ b/configure.in @@ -1,20 +1,14 @@ # Process this file with autoconf to produce a configure script. AC_REVISION($Revision$) AC_PREREQ(2.57) -AC_INIT(mod_auth_kerb, 5.4, modauthkerb-developers@lists.sourceforge.net) -AC_CONFIG_SRCDIR([src/mod_auth_kerb.c]) +AC_INIT(mod_auth_gssapi, 0.1, modauthkerb-developers@lists.sourceforge.net) +AC_CONFIG_SRCDIR([mod_auth_gssapi.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET -# Checks for libraries. -# FIXME: Replace `main' with a function in `-lresolv': -LIB_resolv="" -AC_CHECK_LIB([resolv], [main], [LIB_resolv=-lresolv]) -AC_SUBST(LIB_resolv) - # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([limits.h netdb.h stddef.h stdlib.h string.h unistd.h]) @@ -24,149 +18,76 @@ AC_CHECK_HEADERS([limits.h netdb.h stddef.h stdlib.h string.h unistd.h]) AC_TYPE_SIZE_T AC_STRUCT_TM -# Checks for library functions. -#AC_FUNC_MALLOC -#AC_FUNC_MEMCMP -#AC_CHECK_FUNCS([gethostbyname memset putenv strcasecmp strchr strdup strerror]) - # -# kerberos5 enviroment +# GSS-API enviroment # SPNEGO_SRCS="" -AC_ARG_WITH(krb5, - AC_HELP_STRING([--with-krb5=dir],[use krb5 in dir]), - [ with_krb5="$withval" ]) - -if test "x$with_krb5" = "xyes" ; then - with_krb5="" +AC_ARG_WITH(gss, + AC_HELP_STRING([--with-gss=dir],[use gss in dir]), + [ with_gss="$withval" ]) +if test "x$with_gss" = "xyes" ; then + with_gss="" fi -if test "x$with_krb5" != "xno" ; then - - AC_PATH_PROG(krb5_config_command,krb5-config,:,[$PATH:/usr/kerberos/bin:/usr/krb5/bin]) - if test $krb5_config_command = : ; then - AC_MSG_ERROR(krb5-config not found) - fi - - ac_save_CPPFLAGS="$CPPFLAGS" - ac_save_LDFLAGS="$LDFLAGS" - ac_save_LIBS="$LIBS" +ac_save_CPPFLAGS="$CPPFLAGS" +ac_save_LDFLAGS="$LDFLAGS" +ac_save_LIBS="$LIBS" - if test -n "$with_krb5"; then - if test -x "$with_krb5/bin/krb5-config"; then - krb5_config_command="$with_krb5/bin/krb5-config" - else - krb5_config_command="" - fi - fi - - dnl - dnl find header files - dnl - - if test -z "$KRB5_CPPFLAGS"; then - TMP_KRB5_CPPFLAGS="" - if test -n "$krb5_config_command"; then - TMP_KRB5_CPPFLAGS=`$krb5_config_command --cflags gssapi 2>/dev/null` - fi - if test -z "$TMP_KRB5_CPPFLAGS" -a -n "with_krb5"; then - TMP_KRB5_CPPFLAGS="-I$with_krb5/include" - fi - CPPFLAGS="$CPPFLAGS $TMP_KRB5_CPPFLAGS" - AC_CHECK_HEADERS(gssapi.h, - [ KRB5_CPPFLAGS="$TMP_KRB5_CPPFLAGS" ]) - if test -z "$KRB5_CPPFLAGS"; then - AC_CHECK_HEADERS(gssapi/gssapi.h, - [ KRB5_CPPFLAGS="$TMP_KRB5_CPPFLAGS" ]) - fi - CPPFLAGS="$ac_save_CPPFLAGS" - fi - - dnl - dnl find libraries - dnl - - if test -z "$KRB5_LDFLAGS" -a -n "$krb5_config_command"; then - TMP_KRB5_LDFLAGS=`$krb5_config_command --libs gssapi 2>/dev/null` - if test -n "$TMP_KRB5_LDFLAGS"; then - LIBS="$LIBS $TMP_KRB5_LDFLAGS $LIB_resolv" - AC_CHECK_LIB(krb5, krb5_init_context, - [ KRB5_LDFLAGS="$TMP_KRB5_LDFLAGS" ]) - LIBS="$ac_save_LIBS" - fi - fi - if test "x$with_krb5"="x"; then - tmp_lf_inc="" - else - tmp_lf_inc="-L" - fi - if test -z "$KRB5_LDFLAGS"; then - #try MIT - TMP_KRB5_LDFLAGS="$tmp_lf_inc$with_krb5/lib -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err" - LIBS="$LIBS $TMP_KRB5_LDFLAGS $LIB_resolv" - AC_CHECK_LIB(krb5, krb5_init_context, [ KRB5_LDFLAGS="$TMP_KRB5_LDFLAGS" ]) - LIBS="$ac_save_LIBS" - fi - - if test -z "$KRB5_LDFLAGS"; then - #Try Heimdal - TMP_KRB5_LDFLAGS="$tmp_lf_inc$with_krb5/lib -lgssapi -lkrb5 -lasn1 -lcrypto -lroken -lcrypt" - LIBS="$LIBS $TMP_KRB5_LDFLAGS $LIB_resolv" - AC_CHECK_LIB(krb5, krb5_init_context, [ KRB5_LDFLAGS="$TMP_KRB5_LDFLAGS" ]) - LIBS="$ac_save_LIBS" - fi - - if test -z "$KRB5_LDFLAGS"; then - #Try Heimdal on OpenBSD - TMP_KRB5_LDFLAGS="$tmp_lf_inc$with_krb5/lib -lgssapi -lkrb5 -lasn1 -ldes -lcrypto" - LIBS="$LIBS $TMP_KRB5_LDFLAGS $LIB_resolv" - AC_CHECK_LIB(krb5, krb5_init_context, [ KRB5_LDFLAGS="$TMP_KRB5_LDFLAGS" ]) - LIBS="$ac_save_LIBS" - fi +if test -z "$GSS_CPPFLAGS"; then + TMP_GSS_CPPFLAGS="" + if test -n "with_gss"; then + TMP_GSS_CPPFLAGS="-I$with_gss/include" + fi + CPPFLAGS="$CPPFLAGS $TMP_GSS_CPPFLAGS" + AC_CHECK_HEADERS(gssapi.h, + [ GSS_CPPFLAGS="$TMP_GSS_CPPFLAGS" ]) + if test -z "$GSS_CPPFLAGS"; then + AC_CHECK_HEADERS(gssapi/gssapi.h, + [ GSS_CPPFLAGS="$TMP_GSS_CPPFLAGS" ]) + fi + CPPFLAGS="$ac_save_CPPFLAGS" +fi - if test -z "$KRB5_LDFLAGS"; then - with_krb5=no - else - AC_DEFINE(KRB5) - AC_CHECK_LIB(krb5, - krb5_cc_new_unique, - [ AC_DEFINE(HAVE_KRB5_CC_NEW_UNIQUE) ]) +#if test "x$with_gss"="x"; then +# tmp_lf_inc="" +# else +# tmp_lf_inc="-L" +#fi +if test -z "$GSS_LDFLAGS"; then + TMP_GSS_LDFLAGS="" + if test -n "$with_gss"; then +# XXX think about lib64, etc + TMP_GSS_LDFLAGS="-L $with_gss/lib" + fi - # check for Heimdal - have_heimdal="" - AC_MSG_CHECKING(whether we are using Heimdal) - ac_save_CFLAGS="$CFLAGS" - CFLAGS="$KRB5_CPPFLAGS" - AC_TRY_COMPILE([#include ], - [ char *tmp = heimdal_version; ], - [ AC_MSG_RESULT(yes) - AC_DEFINE(HEIMDAL) - have_heimdal=yes ], - [ AC_MSG_RESULT(no) ]) - CFLAGS="$ac_save_CFLAGS" + LIBS="$LIBS $TMP_GSS_LDFLAGS" + AC_CHECK_LIB(gssapi, gss_accept_sec_context, [ GSS_LDFLAGS="$TMP_GSS_LDFLAGS" ]) + LIBS="$ac_save_LIBS" +fi +if test -z "$GSS_LDFLAGS"; then + AC_MSG_ERROR([failed to find a GSS-API library]) +fi # If SPNEGO is supported by the gssapi libraries, we shouln't build our support. # SPNEGO is supported as of Heimdal 0.7, and MIT 1.5. - gssapi_supports_spnego="" - AC_MSG_CHECKING(whether the GSSAPI libraries support SPNEGO) +gssapi_supports_spnego="" +AC_MSG_CHECKING(whether the GSSAPI libraries support SPNEGO) - ac_save_CFLAGS="$CFLAGS" - CFLAGS="$KRB5_CPPFLAGS" - ac_save_LDFLAGS="$LDFLAGS" - if test -n "$with_krb5"; then - LDFLAGS="$KRB5_LDFLAGS -Wl,-rpath -Wl,$with_krb5/lib" - else - LDFLAGS="$KRB5_LDFLAGS" - fi +ac_save_CFLAGS="$CFLAGS" +CFLAGS="$GSS_CPPFLAGS" + +ac_save_LDFLAGS=$LDFLAGS +LDFLAGS="$GSS_LDFLAGS" +# to make sure the testing binary can be launched: +if test -n "$with_gss"; then + LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,$with_gss/lib" +fi +#XXX schovat nebo dat primo do LDFLAGS: +LIBS=-lgssapi AC_TRY_RUN([ #include -#include -#ifdef HEIMDAL #include -#else -#include -#endif int main(int argc, char** argv) { OM_uint32 major_status, minor_status; @@ -192,31 +113,21 @@ int main(int argc, char** argv) return (!SPNEGO); } }], - [ if test $? -eq 0; then - AC_MSG_RESULT(yes) - AC_DEFINE(GSSAPI_SUPPORTS_SPNEGO) - gssapi_supports_spnego=yes - else - AC_MSG_RESULT(no) - fi], - [AC_MSG_RESULT(no)]) - - CFLAGS="$ac_save_CFLAGS" - LDFLAGS="$ac_save_LDFLAGS" - - if test -z "$gssapi_supports_spnego"; then - if test -n "$have_heimdal"; then SPNEGO_SRCS="\ - spnegokrb5/asn1_MechType.c \ - spnegokrb5/asn1_MechTypeList.c \ - spnegokrb5/asn1_ContextFlags.c \ - spnegokrb5/asn1_NegTokenInit.c \ - spnegokrb5/asn1_NegTokenTarg.c \ - spnegokrb5/init_sec_context.c \ - spnegokrb5/accept_sec_context.c \ - spnegokrb5/encapsulate.c \ - spnegokrb5/decapsulate.c \ - spnegokrb5/external.c" - else SPNEGO_SRCS="\ + [ if test $? -eq 0; then + AC_MSG_RESULT(yes) + AC_DEFINE(GSSAPI_SUPPORTS_SPNEGO) + gssapi_supports_spnego=yes + else + AC_MSG_RESULT(no) + fi], + [ AC_MSG_RESULT(no)] + ) + +CFLAGS="$ac_save_CFLAGS" +LDFLAGS="$ac_save_LDFLAGS" + +if test -z "$gssapi_supports_spnego"; then + SPNEGO_SRCS="\ spnegokrb5/asn1_MechType.c \ spnegokrb5/asn1_MechTypeList.c \ spnegokrb5/asn1_ContextFlags.c \ @@ -233,104 +144,12 @@ int main(int argc, char** argv) spnegokrb5/encapsulate.c \ spnegokrb5/decapsulate.c \ spnegokrb5/external.c" - fi - fi - fi fi -AC_SUBST(KRB5_CPPFLAGS) -AC_SUBST(KRB5_LDFLAGS) +AC_SUBST(GSS_CPPFLAGS) +AC_SUBST(GSS_LDFLAGS) AC_SUBST(SPNEGO_SRCS) # -# Kerberos4 enviroment -# - -AC_ARG_WITH(krb4, - AC_HELP_STRING([--with-krb4=dir], [use krb4 in dir]), - [ with_krb4="$withval" ]) - -if test "x$with_krb4" = "xyes" ; then - with_krb4="" -fi - -if test "x$with_krb4" != "xno"; then - AC_MSG_CHECKING([for Kerberos4 installation]) - if test "x$with_krb4" != "x"; then - if test -x "$with_krb4/bin/krb4-config"; then - KRB4_CPPFLAGS=`$with_krb4/bin/krb4-config --cflags krb4 2>/dev/null` - KRB4_LDFLAGS=`$with_krb4/bin/krb4-config --libs krb4 2>/dev/null` - elif test -x "$with_krb4/bin/krb5-config"; then - KRB4_CPPFLAGS=`$with_krb4/bin/krb5-config --cflags krb4 2>/dev/null` - KRB4_LDFLAGS=`$with_krb4/bin/krb5-config --libs krb4 2>/dev/null` - else - AC_MSG_ERROR([failed to find krb4-config or krb5-config in $with_krb4/bin]) - fi - else - KRB4_CPPFLAGS=`krb4-config --cflags krb4 2>/dev/null` - KRB4_LDFLAGS=`krb4-config --libs krb4 2>/dev/null` - if test "x$KRB4_LDFLAGS" = "x" -a -n "$krb5_config_command"; then - KRB4_CPPFLAGS=`$krb5_config_command --cflags krb4 2>/dev/null` - KRB4_LDFLAGS=`$krb5_config_command --libs krb4 2>/dev/null` - if test $? -ne 0; then - KRB4_CPPFLAGS="" - KRB4_LDFLAGS="" - fi - fi - fi - if test "x$KRB4_LDFLAGS" = "x"; then - with_krb4=no - AC_MSG_RESULT(no) - else - AC_MSG_RESULT(yes) - - ac_save_CFLAGS=$CFLAGS - ac_save_CPPFLAGS=$CPPFLAGS - ac_save_LDFLAGS=$LDFLAGS - ac_save_LIBS=$LIBS - CFLAGS="$CFLAGS $KRB4_CPPFLAGS" - CPPFLAGS="$CFLAGS $KRB4_CPPFLAGS" - LDFLAGS="$LDFLAGS $KRB4_LDFLAGS" - LIBS="$LIBS $LDFLAGS $LIB_resolv" - -# if not found krb.h suppose it's in the kerberosIV subdirectory - AC_CHECK_HEADER(krb.h, , with_krb4=no) - if test "x$with_krb4" = "xno"; then - KRB4_CPPFLAGS="$KRB4_CPPFLAGS ${KRB4_CPPFLAGS}/kerberosIV" -# second run of AC_CHECK_HEADER for the same library returns the cached -# result created by previous run :-( - AC_CHECK_HEADER(kerberosIV/krb.h, with_krb4=yes, - [ with_krb4=no - KRB4_CPPFLAGS="" - KRB4_LDFLAGS=""]) - fi - - if test "x$with_krb4" != "xno"; then - AC_CHECK_LIB(krb4, krb_get_pw_in_tkt, [], [with_krb4=no]) - if test "x$with_krb4" = "xno"; then - AC_CHECK_LIB(krb, krb_get_pw_in_tkt, [with_krb4=yes], - [with_krb4=no - KRB4_CPPFLAGS="" - KRB4_LDFLAGS=""]) - fi - fi - - CFLAGS=$ac_save_CFLAGS - CPPFLAGS=$ac_save_CPPFLAGS - LDFLAGS=$ac_save_LDFLAGS - LIBS=$ac_save_LIBS - - AC_DEFINE(KRB4) - fi -fi -AC_SUBST(KRB4_CPPFLAGS) -AC_SUBST(KRB4_LDFLAGS) - - -if test "x$with_krb5" = "xno" -a "x$with_krb4" = "xno"; then - AC_MSG_ERROR([No Kerberos enviroment found]) -fi - -# # Apache enviroment # AC_ARG_WITH(apache, diff --git a/gss.c b/gss.c new file mode 100644 index 0000000..6b04e53 --- /dev/null +++ b/gss.c @@ -0,0 +1,322 @@ +#include "mod_auth_gssapi.h" + +static const char * +get_gss_error(request_rec *r, OM_uint32 err_maj, OM_uint32 err_min, char *prefix) +{ + OM_uint32 maj_stat, min_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string; + char *err_msg; + int first_pass; + + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "GSS-API major_status:%8.8x, minor_status:%8.8x", + err_maj, err_min); + + err_msg = apr_pstrdup(r->pool, prefix); + do { + maj_stat = gss_display_status (&min_stat, + err_maj, + GSS_C_GSS_CODE, + GSS_C_NO_OID, + &msg_ctx, + &status_string); + if (!GSS_ERROR(maj_stat)) { + err_msg = apr_pstrcat(r->pool, err_msg, + ": ", (char*) status_string.value, NULL); + gss_release_buffer(&min_stat, &status_string); + first_pass = 0; + } + } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); + + msg_ctx = 0; + err_msg = apr_pstrcat(r->pool, err_msg, " (", NULL); + first_pass = 1; + do { + maj_stat = gss_display_status (&min_stat, + err_min, + GSS_C_MECH_CODE, + GSS_C_NULL_OID, + &msg_ctx, + &status_string); + if (!GSS_ERROR(maj_stat)) { + err_msg = apr_pstrcat(r->pool, err_msg, + (first_pass) ? "" : ", ", + (char *) status_string.value, + NULL); + gss_release_buffer(&min_stat, &status_string); + first_pass = 0; + } + } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); + err_msg = apr_pstrcat(r->pool, err_msg, ")", NULL); + + return err_msg; +} + +static int +get_gss_creds(request_rec *r, + gss_auth_config *conf, + gss_cred_id_t *server_creds) +{ + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major_status, minor_status, minor_status2; + gss_name_t server_name = GSS_C_NO_NAME; + char buf[1024]; + int have_server_princ; + + if (conf->service_name && strcmp(conf->service_name, "Any") == 0) { + *server_creds = GSS_C_NO_CREDENTIAL; + return 0; + } + + have_server_princ = conf->service_name && strchr(conf->service_name, '/') != NULL; + if (have_server_princ) + strncpy(buf, conf->service_name, sizeof(buf)); + else + snprintf(buf, sizeof(buf), "%s@%s", + (conf->service_name) ? conf->service_name : SERVICE_NAME, + ap_get_server_name(r)); + + token.value = buf; + token.length = strlen(buf) + 1; + + major_status = gss_import_name(&minor_status, &token, + (have_server_princ) ? (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME : (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, + &server_name); + memset(&token, 0, sizeof(token)); + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "gss_import_name() failed")); + return HTTP_INTERNAL_SERVER_ERROR; + } + + major_status = gss_display_name(&minor_status, server_name, &token, NULL); + if (GSS_ERROR(major_status)) { + /* Perhaps we could just ignore this error but it's safer to give up now, + I think */ + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "gss_display_name() failed")); + return HTTP_INTERNAL_SERVER_ERROR; + } + + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s", token.value); + gss_release_buffer(&minor_status, &token); + + major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_ACCEPT, + server_creds, NULL, NULL); + gss_release_name(&minor_status2, &server_name); + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "Failed to load GSS-API credentials")); + return HTTP_INTERNAL_SERVER_ERROR; + } + + return 0; +} + +static int +cmp_gss_type(gss_buffer_t token, gss_OID oid) +{ + unsigned char *p; + size_t len; + + if (token->length == 0) + return GSS_S_DEFECTIVE_TOKEN; + + p = token->value; + if (*p++ != 0x60) + return GSS_S_DEFECTIVE_TOKEN; + len = *p++; + if (len & 0x80) { + if ((len & 0x7f) > 4) + return GSS_S_DEFECTIVE_TOKEN; + p += len & 0x7f; + } + if (*p++ != 0x06) + return GSS_S_DEFECTIVE_TOKEN; + + if (((OM_uint32) *p++) != oid->length) + return GSS_S_DEFECTIVE_TOKEN; + + return memcmp(p, oid->elements, oid->length); +} + +int +gss_authenticate(request_rec *r, gss_auth_config *conf, gss_conn_ctx ctx, + const char *auth_line, char **negotiate_ret_value) +{ + OM_uint32 major_status, minor_status, minor_status2; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + const char *auth_param = NULL; + int ret; + gss_name_t client_name = GSS_C_NO_NAME; + gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; + gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; + OM_uint32 ret_flags = 0; + gss_OID_desc spnego_oid; + OM_uint32 (*accept_sec_context) + (OM_uint32 *, gss_ctx_id_t *, const gss_cred_id_t, + const gss_buffer_t, const gss_channel_bindings_t, + gss_name_t *, gss_OID *, gss_buffer_t, OM_uint32 *, + OM_uint32 *, gss_cred_id_t *); + + *negotiate_ret_value = "\0"; + + spnego_oid.length = 6; + spnego_oid.elements = (void *)"\x2b\x06\x01\x05\x05\x02"; + + if (conf->krb5_keytab) { + char *ktname; + /* we don't use the ap_* calls here, since the string passed to putenv() + * will become part of the enviroment and shouldn't be free()ed by apache + */ + ktname = malloc(strlen("KRB5_KTNAME=") + strlen(conf->krb5_keytab) + 1); + if (ktname == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, "malloc() failed: not enough memory"); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + sprintf(ktname, "KRB5_KTNAME=%s", conf->krb5_keytab); + putenv(ktname); +#ifdef HEIMDAL + /* Seems to be also supported by latest MIT */ + gsskrb5_register_acceptor_identity(conf->krb_5_keytab); +#endif + } + + ret = get_gss_creds(r, conf, &server_creds); + if (ret) + goto end; + + /* ap_getword() shifts parameter */ + auth_param = ap_getword_white(r->pool, &auth_line); + if (auth_param == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "No Authorization parameter in request from client"); + ret = HTTP_UNAUTHORIZED; + goto end; + } + + if (ctx->state == GSS_CTX_ESTABLISHED) { + gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER); + ctx->context = GSS_C_NO_CONTEXT; + ctx->state = GSS_CTX_EMPTY; + } + + input_token.length = apr_base64_decode_len(auth_param) + 1; + input_token.value = apr_pcalloc(r->connection->pool, input_token.length); + if (input_token.value == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "ap_pcalloc() failed (not enough memory)"); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + input_token.length = apr_base64_decode(input_token.value, auth_param); + + /* LOG length, type */ + +#ifdef GSSAPI_SUPPORTS_SPNEGO + accept_sec_context = gss_accept_sec_context; +#else + accept_sec_context = (cmp_gss_type(&input_token, &spnego_oid) == 0) ? + gss_accept_sec_context_spnego : gss_accept_sec_context; +#endif + + major_status = accept_sec_context(&minor_status, + &ctx->context, + server_creds, + &input_token, + GSS_C_NO_CHANNEL_BINDINGS, + NULL, + NULL, + &output_token, + &ret_flags, + NULL, + &delegated_cred); + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "Client %s us their credential", + (ret_flags & GSS_C_DELEG_FLAG) ? "delegated" : "didn't delegate"); + if (output_token.length) { + char *token = NULL; + size_t len; + + len = apr_base64_encode_len(output_token.length) + 1; + token = apr_pcalloc(r->connection->pool, len + 1); + if (token == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "ap_pcalloc() failed (not enough memory)"); + ret = HTTP_INTERNAL_SERVER_ERROR; + gss_release_buffer(&minor_status2, &output_token); + goto end; + } + apr_base64_encode(token, output_token.value, output_token.length); + token[len] = '\0'; + *negotiate_ret_value = token; + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "GSS-API token of length %d bytes will be sent back", + output_token.length); + gss_release_buffer(&minor_status2, &output_token); + } + + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "Failed to establish authentication")); + /* Don't offer the Negotiate method again if call to GSS layer failed */ + /* XXX ... which means we don't return the "error" output */ + *negotiate_ret_value = NULL; + ret = HTTP_UNAUTHORIZED; + goto end; + } + + if (major_status & GSS_S_CONTINUE_NEEDED) { + ctx->state = GSS_CTX_IN_PROGRESS; + ret = HTTP_UNAUTHORIZED; + goto end; + } + + major_status = gss_inquire_context(&minor_status, ctx->context, &client_name, + NULL, NULL, NULL, NULL, NULL, NULL); + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, "gss_inquire_context() failed")); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); + gss_release_name(&minor_status, &client_name); + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "gss_display_name() failed")); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + ctx->state = GSS_CTX_ESTABLISHED; + ctx->user = apr_pstrdup(r->pool, output_token.value); + gss_release_buffer(&minor_status, &output_token); + + ret = OK; + +end: + if (delegated_cred) + gss_release_cred(&minor_status, &delegated_cred); + + if (output_token.length) + gss_release_buffer(&minor_status, &output_token); + + if (client_name != GSS_C_NO_NAME) + gss_release_name(&minor_status, &client_name); + + if (server_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&minor_status, &server_creds); + + return ret; +} diff --git a/mod_auth_gssapi.c b/mod_auth_gssapi.c new file mode 100644 index 0000000..fb14416 --- /dev/null +++ b/mod_auth_gssapi.c @@ -0,0 +1,189 @@ +#include "mod_auth_gssapi.h" + +module AP_MODULE_DECLARE_DATA auth_gssapi_module; + +#define command(name, func, var, type, usage) \ + AP_INIT_ ## type (name, (void*) func, \ + (void*)APR_OFFSETOF(gss_auth_config, var), \ + OR_AUTHCFG | RSRC_CONF, usage) + +static const command_rec gss_config_cmds[] = { + command("GSSServiceName", ap_set_string_slot, service_name, + TAKE1, "Service name used for Apache authentication."), + + command("GSSKrb5Keytab", ap_set_string_slot, krb5_keytab, + TAKE1, "Location of Kerberos V5 keytab file."), + + { NULL } +}; + +static void * +gss_config_dir_create(apr_pool_t *p, char *d) +{ + gss_auth_config *conf; + + conf = (gss_auth_config *) apr_pcalloc(p, sizeof(*conf)); + return conf; +} + +void +gss_log(const char *file, int line, int level, int status, + const request_rec *r, const char *fmt, ...) +{ + char errstr[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(errstr, sizeof(errstr), fmt, ap); + va_end(ap); + + ap_log_rerror(file, line, level | APLOG_NOERRNO, status, r, "%s", errstr); +} + +static void +set_http_headers(request_rec *r, const gss_auth_config *conf, + char *negotiate_ret_value) +{ + char *negoauth_param; + const char *header_name = (r->proxyreq == PROXYREQ_PROXY) ? + "Proxy-Authenticate" : "WWW-Authenticate"; + + if (negotiate_ret_value == NULL) + return; + + negoauth_param = (*negotiate_ret_value == '\0') ? "GSSAPI" : + apr_pstrcat(r->pool, "GSSAPI ", negotiate_ret_value, NULL); + apr_table_add(r->err_headers_out, header_name, negoauth_param); +} + +static apr_status_t +cleanup_conn_ctx(void *data) +{ + gss_conn_ctx ctx = (gss_conn_ctx) data; + OM_uint32 minor_status; + + if (ctx && ctx->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER); + + return APR_SUCCESS; +} + +static gss_conn_ctx +gss_get_conn_ctx(request_rec *r) +{ + char key[1024]; + gss_conn_ctx ctx = NULL; + + snprintf(key, sizeof(key), "mod_auth_gssapi:conn_ctx"); + apr_pool_userdata_get((void **)&ctx, key, r->connection->pool); + /* XXX LOG */ + if (ctx == NULL) { + ctx = (gss_conn_ctx) apr_palloc(r->connection->pool, sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->context = GSS_C_NO_CONTEXT; + ctx->state = GSS_CTX_EMPTY; + ctx->user = NULL; + apr_pool_userdata_set(ctx, key, cleanup_conn_ctx, r->connection->pool); + } + return ctx; +} + +static int +gss_authenticate_user(request_rec *r) +{ + gss_auth_config *conf = + (gss_auth_config *) ap_get_module_config(r->per_dir_config, + &auth_gssapi_module); + const char *auth_line = NULL; + const char *type = NULL; + char *auth_type = NULL; + char *negotiate_ret_value = NULL; + gss_conn_ctx conn_ctx = NULL; + int ret; + + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Entering GSSAPI authentication"); + + /* get the type specified in Apache configuration */ + type = ap_auth_type(r); + if (type == NULL || strcmp(type, "GSSAPI") != 0) { + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "AuthType '%s' is not for us, bailing out", + (type) ? type : "(NULL)"); + + return DECLINED; + } + + /* get what the user sent us in the HTTP header */ + auth_line = apr_table_get(r->headers_in, (r->proxyreq == PROXYREQ_PROXY) + ? "Proxy-Authorization" + : "Authorization"); + if (auth_line == NULL) { + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "Client hasn't sent any authentication data, giving up"); + set_http_headers(r, conf, "\0"); + return HTTP_UNAUTHORIZED; + } + + auth_type = ap_getword_white(r->pool, &auth_line); + if (strcasecmp(auth_type, "GSSAPI") != 0) { + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "Unsupported authentication type (%s) requested by client", + (auth_type) ? auth_type : "(NULL)"); + set_http_headers(r, conf, "\0"); + return HTTP_UNAUTHORIZED; + } + + conn_ctx = gss_get_conn_ctx(r); + if (conn_ctx == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "Failed to create internal context: probably not enough memory"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* optimizing hack */ + if (conn_ctx->state == GSS_CTX_ESTABLISHED && auth_line == NULL) { + r->user = apr_pstrdup(r->pool, conn_ctx->user); + r->ap_auth_type = "GSSAPI"; + return OK; + } + + /* XXXX subrequests ignored, only successful accesses taken into account! */ + if (!ap_is_initial_req(r) && conn_ctx->state == GSS_CTX_ESTABLISHED) { + r->user = apr_pstrdup(r->pool, conn_ctx->user); + r->ap_auth_type = "GSSAPI"; + return OK; + } + + ret = gss_authenticate(r, conf, conn_ctx, + auth_line, &negotiate_ret_value); + if (ret == HTTP_UNAUTHORIZED || ret == OK) { + /* LOG?? */ + set_http_headers(r, conf, negotiate_ret_value); + } + + if (ret == OK) { + r->user = apr_pstrdup(r->pool, conn_ctx->user); + r->ap_auth_type = "GSSAPI"; + } + + /* debug LOG ??? */ + + return ret; +} + +static void +gss_register_hooks(apr_pool_t *p) +{ + ap_hook_check_user_id(gss_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE); +} + +module AP_MODULE_DECLARE_DATA auth_gssapi_module = { + STANDARD20_MODULE_STUFF, + gss_config_dir_create, + NULL, + NULL, + NULL, + gss_config_cmds, + gss_register_hooks +}; diff --git a/mod_auth_gssapi.h b/mod_auth_gssapi.h new file mode 100644 index 0000000..a8f8a8d --- /dev/null +++ b/mod_auth_gssapi.h @@ -0,0 +1,46 @@ +#ifndef __MOD_AUTH_GSSAPI_H__ +#define __MOD_AUTH_GSSAPI_H__ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +/* XXX */ +#define GSS_KRB5_NT_PRINCIPAL_NAME 0xdeaddead + +#ifndef GSSAPI_SUPPORTS_SPNEGO +#include "spnegokrb5.h" +#endif + +#define SERVICE_NAME "HTTP" + +typedef struct { + const char *service_name; + const char *krb5_keytab; +} gss_auth_config; + +typedef struct gss_conn_ctx_t { + gss_ctx_id_t context; + enum { + GSS_CTX_EMPTY, + GSS_CTX_IN_PROGRESS, + GSS_CTX_ESTABLISHED, + } state; + char *user; +} *gss_conn_ctx; + +void +gss_log(const char *file, int line, int level, int status, + const request_rec *r, const char *fmt, ...); + +int +gss_authenticate(request_rec *r, gss_auth_config *conf, gss_conn_ctx ctx, + const char *auth_line, char **negotiate_ret_value); +#endif -- 2.1.4