From b27fce04e29ac8cf2fb9cff709989873daba3e25 Mon Sep 17 00:00:00 2001 From: kouril Date: Fri, 5 Aug 2005 15:16:29 +0000 Subject: [PATCH] - Don't build the SPNEGO library at all if using latest heimdal (or another distributions supporting SPNEGO, are there any?) - Changed the semantics of the KrbServiceName directive. It can contain not only the service name (HTTP) but also a full principal name that will be used for authentication of the server. This should help in solving some DNS issues. --- README | 10 ++++-- config.h.in | 3 ++ configure.in | 31 +++++++++++++---- spnegokrb5/spnegokrb5.h | 8 +++++ src/mod_auth_kerb.c | 90 +++++++++++++++++++++++++++++++------------------ 5 files changed, 100 insertions(+), 42 deletions(-) diff --git a/README b/README index 9963158..4303e80 100644 --- a/README +++ b/README @@ -59,9 +59,13 @@ KrbVerifyKDC on | off (set to on by default) keytab to prevent KDC spoofing atacks. It should be used only for testing purposes. You have been warned. -KrbServiceName service (set to HTTP by default) - For specification the service name that will be used by Apache for - authentication. Corresponding key of this name must be stored in the keytab. +KrbServiceName server_principal + Specifies a principal name to use by Apache when authenticating the clients. + By default value of the form + HTTP/@ + is used. The FQDN part can contain any hostname and can be used to work + around problems with misconfigured DNS. A corresponding key of this name + must be stored in the keytab. Krb4Srvtab /path/to/srvtab This option takes one argument, specifying the path to the Kerberos V4 diff --git a/config.h.in b/config.h.in index c05a9cd..fec7ca1 100644 --- a/config.h.in +++ b/config.h.in @@ -14,3 +14,6 @@ /* Define if you want to enable support for Kerberos4 */ #undef KRB4 + +/* Define if your GSSAPI library supports handling SPNEGO tokens */ +#undef GSSAPI_SUPPORTS_SPNEGO diff --git a/configure.in b/configure.in index 76da0fc..ec17a99 100644 --- a/configure.in +++ b/configure.in @@ -126,13 +126,32 @@ if test "x$with_krb5" != "xno" ; then AC_TRY_COMPILE([#include ], [ char *tmp = heimdal_version; ], [ AC_MSG_RESULT(yes) - AC_DEFINE(HEIMDAL) ] - have_heimdal=yes, + AC_DEFINE(HEIMDAL) + have_heimdal=yes ], [ AC_MSG_RESULT(no) ]) CFLAGS="$ac_save_CFLAGS" - if test -n "$have_heimdal"; then - SPNEGO_SRCS="\ +# If SPNEGO is supported by the gssapi libraries, we shouln't build our support. +# SPNEGO is supported as of Heimdal 0.7, don't know about MIT. + gssapi_supports_spnego="" + AC_MSG_CHECKING(whether the GSSAPI libraries support SPNEGO) + # Invent some better test + if test "$have_heimdal" = yes; then + ac_save_CFLAGS="$CFLAGS" + CFLAGS="$KRB5_CPPFLAGS" + AC_TRY_COMPILE([#include ], + [ gss_OID oid = GSS_SPNEGO_MECHANISM; ], + [ AC_MSG_RESULT(yes) + AC_DEFINE(GSSAPI_SUPPORTS_SPNEGO) + gssapi_supports_spnego=yes ], + [ AC_MSG_RESULT(no) ]) + CFLAGS="$ac_save_CFLAGS" + else + AC_MSG_RESULT(no) + fi + + 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 \ @@ -143,8 +162,7 @@ if test "x$with_krb5" != "xno" ; then spnegokrb5/encapsulate.c \ spnegokrb5/decapsulate.c \ spnegokrb5/external.c" - else - SPNEGO_SRCS="\ + else SPNEGO_SRCS="\ spnegokrb5/asn1_MechType.c \ spnegokrb5/asn1_MechTypeList.c \ spnegokrb5/asn1_ContextFlags.c \ @@ -161,6 +179,7 @@ if test "x$with_krb5" != "xno" ; then spnegokrb5/encapsulate.c \ spnegokrb5/decapsulate.c \ spnegokrb5/external.c" + fi fi fi fi diff --git a/spnegokrb5/spnegokrb5.h b/spnegokrb5/spnegokrb5.h index 5773070..84359ee 100644 --- a/spnegokrb5/spnegokrb5.h +++ b/spnegokrb5/spnegokrb5.h @@ -12,6 +12,14 @@ extern "C" { # include #endif +#ifndef KRB5_LIB_FUNCTION +# if defined(_WIN32) +# define KRB5_LIB_FUNCTION _stdcall +# else +# define KRB5_LIB_FUNCTION +# endif +#endif + OM_uint32 KRB5_LIB_FUNCTION gss_init_sec_context_spnego( OM_uint32 *, const gss_cred_id_t, diff --git a/src/mod_auth_kerb.c b/src/mod_auth_kerb.c index 9624ef4..de9bfa7 100644 --- a/src/mod_auth_kerb.c +++ b/src/mod_auth_kerb.c @@ -51,7 +51,9 @@ #include #define MODAUTHKERB_VERSION "5.0-rc6" + #define MECH_NEGOTIATE "Negotiate" +#define SERVICE_NAME "HTTP" #include #include @@ -88,13 +90,6 @@ #define snprintf _snprintf #endif -#ifndef KRB5_LIB_FUNCTION -# if defined(_WIN32) -# define KRB5_LIB_FUNCTION _stdcall -# else -# define KRB5_LIB_FUNCTION -#endif - #ifdef KRB5 #include #ifdef HEIMDAL @@ -107,7 +102,9 @@ # define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name # define krb5_get_err_text(context,code) error_message(code) #endif -#include "spnegokrb5.h" +#ifndef GSSAPI_SUPPORTS_SPNEGO +# include "spnegokrb5.h" +#endif #endif /* KRB5 */ #ifdef KRB4 @@ -154,7 +151,7 @@ typedef struct { char *krb_auth_realms; int krb_save_credentials; int krb_verify_kdc; - char *krb_service_name; + const char *krb_service_name; int krb_authoritative; int krb_delegate_basic; #ifdef KRB5 @@ -201,7 +198,7 @@ static const command_rec kerb_auth_cmds[] = { FLAG, "Verify tickets against keytab to prevent KDC spoofing attacks."), command("KrbServiceName", ap_set_string_slot, krb_service_name, - TAKE1, "Service name to be used by Apache for authentication."), + TAKE1, "Full or partial service name to be used by Apache for authentication."), command("KrbAuthoritative", ap_set_flag_slot, krb_authoritative, FLAG, "Set to 'off' to allow access control to be passed along to lower modules iff the UserID is not known to this module."), @@ -304,7 +301,7 @@ static void *kerb_dir_create_config(MK_POOL *p, char *d) rec = (kerb_auth_config *) ap_pcalloc(p, sizeof(kerb_auth_config)); ((kerb_auth_config *)rec)->krb_verify_kdc = 1; - ((kerb_auth_config *)rec)->krb_service_name = "HTTP"; + ((kerb_auth_config *)rec)->krb_service_name = NULL; ((kerb_auth_config *)rec)->krb_authoritative = 1; ((kerb_auth_config *)rec)->krb_delegate_basic = 0; #ifdef KRB5 @@ -481,6 +478,7 @@ authenticate_user_krb4pwd(request_rec *r, realm = lrealm; } + /* XXX conf->krb_service_name */ ret = verify_krb4_user(r, (char *)sent_name, (sent_instance) ? sent_instance : "", (char *)realm, (char *)sent_pw, @@ -655,11 +653,10 @@ end: /* Inspired by krb5_verify_user from Heimdal */ static krb5_error_code verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal, - const char *password, const char *service, krb5_keytab keytab, - int krb_verify_kdc, krb5_ccache *ccache) + const char *password, krb5_principal server, + krb5_keytab keytab, int krb_verify_kdc, krb5_ccache *ccache) { krb5_creds creds; - krb5_principal server = NULL; krb5_error_code ret; krb5_ccache ret_ccache = NULL; char *name = NULL; @@ -687,16 +684,6 @@ verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal, goto end; } - ret = krb5_sname_to_principal(context, ap_get_server_name(r), service, - KRB5_NT_SRV_HST, &server); - if (ret) { - log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "krb5_sname_to_principal() failed: %s", - krb5_get_err_text(context, ret)); - goto end; - } - /* XXX log_debug: lookig for in keytab */ - /* XXX { char *realm; @@ -744,8 +731,6 @@ verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal, end: krb5_free_cred_contents(context, &creds); - if (server) - krb5_free_principal(context, server); if (ret_ccache) krb5_cc_destroy(context, ret_ccache); @@ -892,6 +877,7 @@ authenticate_user_krb5pwd(request_rec *r, krb5_context kcontext = NULL; krb5_error_code code; krb5_principal client = NULL; + krb5_principal server = NULL; krb5_ccache ccache = NULL; krb5_keytab keytab = NULL; int ret; @@ -919,6 +905,34 @@ authenticate_user_krb5pwd(request_rec *r, if (conf->krb_5_keytab) krb5_kt_resolve(kcontext, conf->krb_5_keytab, &keytab); + if (conf->krb_service_name && strchr(conf->krb_service_name, '/') != NULL) + ret = krb5_parse_name (kcontext, conf->krb_service_name, &server); + else + ret = krb5_sname_to_principal(kcontext, ap_get_server_name(r), + (conf->krb_service_name) ? conf->krb_service_name : SERVICE_NAME, + KRB5_NT_SRV_HST, &server); + + if (ret) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Error parsing server name (%s): %s", + (conf->krb_service_name) ? conf->krb_service_name : SERVICE_NAME, + krb5_get_err_text(kcontext, ret)); + ret = HTTP_UNAUTHORIZED; + goto end; + } + + code = krb5_unparse_name(kcontext, server, &name); + if (code) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "krb5_unparse_name() failed: %s", + krb5_get_err_text(kcontext, code)); + ret = HTTP_UNAUTHORIZED; + goto end; + } + log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Using %s as server principal for password verification", name); + free(name); + name = NULL; + p = strchr(sent_name, '@'); if (p) { *p++ = '\0'; @@ -950,9 +964,8 @@ authenticate_user_krb5pwd(request_rec *r, continue; } - code = verify_krb5_user(r, kcontext, client, sent_pw, - conf->krb_service_name, - keytab, conf->krb_verify_kdc, &ccache); + code = verify_krb5_user(r, kcontext, client, sent_pw, + server, keytab, conf->krb_verify_kdc, &ccache); if (!conf->krb_authoritative && code) { /* if we're not authoritative, we allow authentication to pass on * to another modules if (and only if) the user is not known to us */ @@ -1000,6 +1013,8 @@ end: ret, (MK_USER)?MK_USER:"(NULL)", (MK_AUTH_TYPE)?MK_AUTH_TYPE:"(NULL)"); if (client) krb5_free_principal(kcontext, client); + if (server) + krb5_free_principal(kcontext, server); if (ccache) krb5_cc_destroy(kcontext, ccache); if (keytab) @@ -1111,15 +1126,21 @@ get_gss_creds(request_rec *r, OM_uint32 major_status, minor_status, minor_status2; gss_name_t server_name = GSS_C_NO_NAME; char buf[1024]; + int have_server_princ; - snprintf(buf, sizeof(buf), "%s@%s", conf->krb_service_name, - ap_get_server_name(r)); + have_server_princ = conf->krb_service_name && strchr(conf->krb_service_name, '/') != NULL; + if (have_server_princ) + strncpy(buf, conf->krb_service_name, sizeof(buf)); + else + snprintf(buf, sizeof(buf), "%s@%s", + (conf->krb_service_name) ? conf->krb_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, - GSS_C_NT_HOSTBASED_SERVICE, + (have_server_princ) ? GSS_KRB5_NT_PRINCIPAL_NAME : GSS_C_NT_HOSTBASED_SERVICE, &server_name); memset(&token, 0, sizeof(token)); if (GSS_ERROR(major_status)) { @@ -1272,10 +1293,13 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, } input_token.length = ap_base64decode(input_token.value, auth_param); +#ifdef GSSAPI_SUPPORTS_SPNEGO + accept_sec_token = gss_accept_sec_context; +#else accept_sec_token = (cmp_gss_type(&input_token, &spnego_oid) == 0) ? gss_accept_sec_context_spnego : gss_accept_sec_context; +#endif - /* pridat: Read client Negotiate data of length XXX, prefix YYY */ log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Verifying client data using %s", (accept_sec_token == gss_accept_sec_context) ? "KRB5 GSS-API" -- 2.1.4