X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmod_auth_kerb.c;h=91c35ac2308533b673c42de9e042ec6546cd9d66;hb=e751edb2ce10f04613e02a8a2dfb0585506662f6;hp=c035c6e992ff490183e11ccd11da9af75995bc9e;hpb=41ad4f9234d5383ac7b9be7431783546c677c7b4;p=mod_auth_kerb.cvs%2F.git diff --git a/src/mod_auth_kerb.c b/src/mod_auth_kerb.c index c035c6e..91c35ac 100644 --- a/src/mod_auth_kerb.c +++ b/src/mod_auth_kerb.c @@ -1,3 +1,72 @@ +/* + * Daniel Kouril + * + * Source and Documentation can be found at: + * http://modauthkerb.sourceforge.net/ + * + * Based on work by + * James E. Robinson, III + * Daniel Henninger + */ + +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + #ident "$Id$" #ifndef APXS1 @@ -14,9 +83,20 @@ #ifdef KRB5 #include #include +#ifndef HEIMDAL +# include +# define GSS_C_NT_USER_NAME gss_nt_user_name +# define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +# define krb5_get_err_text(context,code) error_message(code) +#endif #endif /* KRB5 */ #ifdef KRB4 +/*Prevent warning about closesocket redefinition (Apache's ap_config.h and + * MIT Kerberos' port-sockets.h both define it as close) */ +#ifdef closesocket +# undef closesocket +#endif #include #endif /* KRB4 */ @@ -55,15 +135,9 @@ module AP_MODULE_DECLARE_DATA auth_kerb_module; ***************************************************************************/ typedef struct { char *krb_auth_realms; - int krb_fail_status; - char *krb_force_instance; int krb_save_credentials; - char *krb_tmp_dir; - char *service_name; - char *krb_lifetime; #ifdef KRB5 char *krb_5_keytab; - int krb_forwardable; int krb_method_gssapi; int krb_method_k5pass; #endif @@ -89,46 +163,21 @@ krb5_save_realms(cmd_parms *cmd, kerb_auth_config *sec, char *arg); #endif static const command_rec kerb_auth_cmds[] = { - command("KrbAuthRealm", krb5_save_realms, krb_auth_realms, - RAW_ARGS, "Realms to attempt authentication against (can be multiple)."), - command("KrbAuthRealms", krb5_save_realms, krb_auth_realms, - RAW_ARGS, "Alias for KrbAuthRealm."), - -#if 0 - command("KrbFailStatus", kerb_set_fail_slot, krb_fail_status, - TAKE1, "If auth fails, return status set here."), -#endif + RAW_ARGS, "Realms to attempt authentication against (can be multiple)."), - command("KrbForceInstance", ap_set_string_slot, krb_force_instance, - TAKE1, "Force authentication against an instance specified here."), + command("KrbAuthRealm", krb5_save_realms, krb_auth_realms, + RAW_ARGS, "Alias for KrbAuthRealms."), command("KrbSaveCredentials", ap_set_flag_slot, krb_save_credentials, FLAG, "Save and store credentials/tickets retrieved during auth."), - command("KrbSaveTickets", ap_set_flag_slot, krb_save_credentials, - FLAG, "Alias for KrbSaveCredentials."), - - command("KrbTmpdir", ap_set_string_slot, krb_tmp_dir, - TAKE1, "Path to store ticket files and such in."), - - command("KrbServiceName", ap_set_string_slot, service_name, - TAKE1, "Kerberos service name to be used by apache."), - -#if 0 - command("KrbLifetime", ap_set_string_slot, krb_lifetime, - TAKE1, "Kerberos ticket lifetime."), -#endif - #ifdef KRB5 command("Krb5Keytab", ap_set_file_slot, krb_5_keytab, TAKE1, "Location of Kerberos V5 keytab file."), - command("KrbForwardable", ap_set_flag_slot, krb_forwardable, - FLAG, "Credentials retrieved will be flagged as forwardable."), - - command("KrbMethodGSSAPI", ap_set_flag_slot, krb_method_gssapi, - FLAG, "Enable GSSAPI authentication."), + command("KrbMethodNegotiate", ap_set_flag_slot, krb_method_gssapi, + FLAG, "Enable Negotiate authentication method."), command("KrbMethodK5Pass", ap_set_flag_slot, krb_method_k5pass, FLAG, "Enable Kerberos V5 password authentication."), @@ -163,7 +212,6 @@ static void *kerb_dir_create_config(MK_POOL *p, char *d) kerb_auth_config *rec; rec = (kerb_auth_config *) ap_pcalloc(p, sizeof(kerb_auth_config)); - ((kerb_auth_config *)rec)->krb_fail_status = HTTP_UNAUTHORIZED; #ifdef KRB5 ((kerb_auth_config *)rec)->krb_method_k5pass = 1; ((kerb_auth_config *)rec)->krb_method_gssapi = 1; @@ -198,112 +246,159 @@ void log_rerror(const char *file, int line, int level, int status, #endif } -#if 0 -static const char *kerb_set_fail_slot(cmd_parms *cmd, void *struct_ptr, - const char *arg) -{ - int offset = (int) (long) cmd->info; - if (!strncasecmp(arg, "unauthorized", 12)) - *(int *) ((char *)struct_ptr + offset) = HTTP_UNAUTHORIZED; - else if (!strncasecmp(arg, "forbidden", 9)) - *(int *) ((char *)struct_ptr + offset) = HTTP_FORBIDDEN; - else if (!strncasecmp(arg, "declined", 8)) - *(int *) ((char *)struct_ptr + offset) = DECLINED; - else - return "KrbAuthFailStatus must be Forbidden, Unauthorized, or Declined."; - return NULL; -} -#endif - #ifdef KRB4 /*************************************************************************** Username/Password Validation for Krb4 ***************************************************************************/ -int kerb4_password_validate(request_rec *r, const char *user, const char *pass) +static int +verify_krb4_user(request_rec *r, char *name, char *instance, char *realm, + char *password, char *linstance, char *srvtab) +{ + int ret; + char *phost; + unsigned long addr; + struct hostent *hp; + const char *hostname; + KTEXT_ST ticket; + AUTH_DAT authdata; + char lrealm[REALM_SZ]; + + ret = krb_get_pw_in_tkt(name, instance, realm, "krbtgt", realm, + DEFAULT_TKT_LIFE, password); + if (ret) + /* log(krb_err_txt[ret]) */ + return ret; + + hostname = ap_get_server_name(r); + + hp = gethostbyname(hostname); + if (hp == NULL) { + dest_tkt(); + return h_errno; + } + memcpy(&addr, hp->h_addr, sizeof(addr)); + + phost = krb_get_phost((char *)hostname); + + krb_get_lrealm(lrealm, 1); + + ret = krb_mk_req(&ticket, linstance, phost, lrealm, 0); + if (ret) { + dest_tkt(); + return ret; + } + + ret = krb_rd_req(&ticket, linstance, phost, addr, &authdata, srvtab); + if (ret) + dest_tkt(); + + return ret; +} + +static int +krb4_cache_cleanup(void *data) { - kerb_auth_config *conf = - (kerb_auth_config *)ap_get_module_config(r->per_dir_config, - &auth_kerb_module); - int ret; - int lifetime = DEFAULT_TKT_LIFE; - char *c, *tfname; - char *username = NULL; - char *instance = NULL; - char *realm = NULL; - - username = (char *)ap_pstrdup(r->pool, user); - if (!username) { - return 0; - } - - instance = strchr(username, '.'); - if (instance) { - *instance++ = '\0'; - } - else { - instance = ""; - } - - realm = strchr(username, '@'); - if (realm) { - *realm++ = '\0'; - } - else { - realm = ""; - } - - if (conf->krb_lifetime) { - lifetime = atoi(conf->krb_lifetime); - } - - if (conf->krb_force_instance) { - instance = conf->krb_force_instance; - } - - if (conf->krb_save_credentials) { - tfname = (char *)malloc(sizeof(char) * MAX_STRING_LEN); - sprintf(tfname, "%s/k5cc_ap_%s", - conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp", - MK_USER); - - if (!strcmp(instance, "")) { - tfname = strcat(tfname, "."); - tfname = strcat(tfname, instance); - } - - if (!strcmp(realm, "")) { - tfname = strcat(tfname, "."); - tfname = strcat(tfname, realm); - } - - for (c = tfname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir : - "/tmp") + 1; *c; c++) { - if (*c == '/') - *c = '.'; - } - - krb_set_tkt_string(tfname); - } - - if (!strcmp(realm, "")) { - realm = (char *)malloc(sizeof(char) * (REALM_SZ + 1)); - ret = krb_get_lrealm(realm, 1); - if (ret != KSUCCESS) - return 0; - } - - ret = krb_get_pw_in_tkt((char *)user, instance, realm, "krbtgt", realm, - lifetime, (char *)pass); - switch (ret) { - case INTK_OK: - case INTK_W_NOTALL: - return 1; - break; - - default: - return 0; - break; - } + char *tkt_file = (char *) data; + + krb_set_tkt_string(tkt_file); + dest_tkt(); + return OK; +} + +static int +authenticate_user_krb4pwd(request_rec *r, + kerb_auth_config *conf, + const char *auth_line) +{ + int ret; + const char *sent_pw; + const char *sent_name; + char *sent_instance; + char tkt_file[32]; + char *tkt_file_p = NULL; + int fd; + const char *realms; + const char *realm; + char *user; + char lrealm[REALM_SZ]; + + sent_pw = ap_pbase64decode(r->pool, auth_line); + sent_name = ap_getword (r->pool, &sent_pw, ':'); + + /* do not allow user to override realm setting of server */ + if (strchr(sent_name, '@')) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "specifying realm in user name is prohibited"); + return HTTP_UNAUTHORIZED; + } + + sent_instance = strchr(sent_name, '.'); + if (sent_instance) + *sent_instance++ = '\0'; + + snprintf(tkt_file, sizeof(tkt_file), "/tmp/apache_tkt_XXXXXX"); + fd = mkstemp(tkt_file); + if (fd < 0) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Cannot create krb4 ccache: mkstemp() failed: %s", + strerror(errno)); + return HTTP_INTERNAL_SERVER_ERROR; + } + + tkt_file_p = ap_pstrdup(r->pool, tkt_file); + ap_register_cleanup(r->pool, tkt_file_p, + krb4_cache_cleanup, ap_null_cleanup); + + krb_set_tkt_string(tkt_file); + + realms = conf->krb_auth_realms; + do { + memset(lrealm, 0, sizeof(lrealm)); + realm = NULL; + if (realms) + realm = ap_getword_white(r->pool, &realms); + + if (realm == NULL) { + ret = krb_get_lrealm(lrealm, 1); + realm = lrealm; + } + if (realm == NULL || *realm == '\0') + break; + + ret = verify_krb4_user(r, (char *)sent_name, + (sent_instance) ? sent_instance : "", + (char *)realm, (char *)sent_pw, "khttp", + conf->krb_4_srvtab); + if (ret == 0) + break; + } while (realms && *realms); + + if (ret) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Verifying krb4 password failed (%d)", ret); + ret = HTTP_UNAUTHORIZED; + goto end; + } + + user = ap_pstrdup(r->pool, sent_name); + if (sent_instance) + user = ap_pstrcat(r->pool, user, ".", sent_instance, NULL); + user = ap_pstrcat(r->pool, user, "@", realm, NULL); + + MK_USER = user; + MK_AUTH_TYPE = "Basic"; + ap_table_setn(r->subprocess_env, "KRBTKFILE", tkt_file_p); + + if (!conf->krb_save_credentials) + krb4_cache_cleanup(tkt_file); + +end: + if (ret) + krb4_cache_cleanup(tkt_file); + close(fd); + tf_close(); + + return ret; } #endif /* KRB4 */ @@ -311,58 +406,49 @@ int kerb4_password_validate(request_rec *r, const char *user, const char *pass) /*************************************************************************** Username/Password Validation for Krb5 ***************************************************************************/ -#ifndef HEIMDAL -krb5_error_code -krb5_verify_user(krb5_context context, krb5_principal principal, - krb5_ccache ccache, const char *password, krb5_boolean secure, - const char *service) +/* Inspired by krb5_verify_user from Heimdal */ +static krb5_error_code +verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal, + krb5_ccache ccache, const char *password, const char *service, + krb5_keytab keytab) { - int ret; - krb5_context kcontext; - krb5_principal server, client; - krb5_timestamp now; - krb5_creds my_creds; - krb5_flags options = 0; - krb5_principal me = NULL; - krb5_data tgtname = { - 0, - KRB5_TGS_NAME_SIZE, - KRB5_TGS_NAME - }; - - memset((char *)&my_creds, 0, sizeof(my_creds)); - my_creds.client = principal; - - if (krb5_build_principal_ext(kcontext, &server, - krb5_princ_realm(kcontext, me)->length, - krb5_princ_realm(kcontext, me)->data, - tgtname.length, tgtname.data, - krb5_princ_realm(kcontext, me)->length, - krb5_princ_realm(kcontext, me)->data, - 0)) { - return ret; - } + krb5_creds creds; + krb5_principal server = NULL; + krb5_error_code ret; + krb5_verify_init_creds_opt opt; - my_creds.server = server; - if (krb5_timeofday(kcontext, &now)) - return -1; + memset(&creds, 0, sizeof(creds)); - my_creds.times.starttime = 0; - /* XXX - my_creds.times.endtime = now + lifetime; - my_creds.times.renew_till = now + renewal; - */ + ret = krb5_get_init_creds_password(context, &creds, principal, + (char *)password, krb5_prompter_posix, + NULL, 0, NULL, NULL); + if (ret) + return ret; - ret = krb5_get_in_tkt_with_password(kcontext, options, 0, NULL, 0, - password, ccache, &my_creds, 0); - if (ret) { - return ret; + ret = krb5_sname_to_principal(context, ap_get_server_name(r), service, + KRB5_NT_SRV_HST, &server); + if (ret) + goto end; + + krb5_verify_init_creds_opt_init(&opt); + krb5_verify_init_creds_opt_set_ap_req_nofail(&opt, 1); + + ret = krb5_verify_init_creds(context, &creds, server, keytab, NULL, &opt); + if (ret) + goto end; + + if (ccache) { + ret = krb5_cc_initialize(context, ccache, principal); + if (ret == 0) + ret = krb5_cc_store_cred(context, ccache, &creds); } - return 0; +end: + krb5_free_cred_contents(context, &creds); + if (server) + krb5_free_principal(context, server); + return ret; } -#endif - static int krb5_cache_cleanup(void *data) @@ -398,53 +484,52 @@ create_krb5_ccache(krb5_context kcontext, krb5_principal princ, krb5_ccache *ccache) { - char *c, ccname[MAX_STRING_LEN]; - krb5_error_code problem; - int ret; - krb5_ccache tmp_ccache = NULL; - - snprintf(ccname, sizeof(ccname), "FILE:%s/k5cc_ap_%s", - conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp", - MK_USER); - - for (c = ccname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir : - "/tmp") + 1; *c; c++) { - if (*c == '/') - *c = '.'; - } - - problem = krb5_cc_resolve(kcontext, ccname, &tmp_ccache); - if (problem) { - log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Cannot create krb5 ccache: krb5_cc_resolve() failed: %s", - krb5_get_err_text(kcontext, problem)); - ret = HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - problem = krb5_cc_initialize(kcontext, tmp_ccache, princ); - if (problem) { - log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Cannot create krb5 ccache: krb5_cc_initialize() failed: %s", - krb5_get_err_text(kcontext, problem)); - ret = HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - ap_table_setn(r->subprocess_env, "KRB5CCNAME", ccname); - ap_register_cleanup(r->pool, ccname, - krb5_cache_cleanup, ap_null_cleanup); - - *ccache = tmp_ccache; - tmp_ccache = NULL; - - ret = OK; + char *ccname; + krb5_error_code problem; + int ret; + krb5_ccache tmp_ccache = NULL; + +#ifdef HEIMDAL + problem = krb5_cc_gen_new(kcontext, &krb5_fcc_ops, &tmp_ccache); +#else + problem = krb5_fcc_generate_new(kcontext, &tmp_ccache); + /* krb5_fcc_generate_new() doesn't set KRB5_TC_OPENCLOSE, which makes + krb5_cc_initialize() fail */ + krb5_fcc_set_flags(kcontext, tmp_ccache, KRB5_TC_OPENCLOSE); +#endif + if (problem) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Cannot create file for new krb5 ccache: %s", + krb5_get_err_text(kcontext, problem)); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + ccname = ap_pstrdup(r->pool, krb5_cc_get_name(kcontext, tmp_ccache)); + + problem = krb5_cc_initialize(kcontext, tmp_ccache, princ); + if (problem) { + log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Cannot initialize krb5 ccache %s: krb5_cc_initialize() failed: %s", + ccname, krb5_get_err_text(kcontext, problem)); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + ap_table_setn(r->subprocess_env, "KRB5CCNAME", ccname); + ap_register_cleanup(r->pool, ccname, + krb5_cache_cleanup, ap_null_cleanup); + + *ccache = tmp_ccache; + tmp_ccache = NULL; + + ret = OK; end: - if (tmp_ccache) - krb5_cc_destroy(kcontext, tmp_ccache); + if (tmp_ccache) + krb5_cc_destroy(kcontext, tmp_ccache); - return ret; + return ret; } static int @@ -472,10 +557,14 @@ store_krb5_creds(krb5_context kcontext, return ret; } +#ifdef HEIMDAL problem = krb5_cc_copy_cache(kcontext, delegated_cred, ccache); +#else + problem = krb5_cc_copy_creds(kcontext, delegated_cred, ccache); +#endif krb5_free_principal(kcontext, princ); if (problem) { - snprintf(errstr, sizeof(errstr), "krb5_cc_copy_cache() failed: %s", + snprintf(errstr, sizeof(errstr), "Failed to store credentials: %s", krb5_get_err_text(kcontext, problem)); krb5_cc_destroy(kcontext, ccache); return HTTP_INTERNAL_SERVER_ERROR; @@ -491,12 +580,15 @@ int authenticate_user_krb5pwd(request_rec *r, const char *auth_line) { const char *sent_pw = NULL; + const char *sent_name = NULL; const char *realms = NULL; krb5_context kcontext = NULL; krb5_error_code code; krb5_principal client = NULL; krb5_ccache ccache = NULL; + krb5_keytab keytab = NULL; int ret; + char *name = NULL; code = krb5_init_context(&kcontext); if (code) { @@ -506,11 +598,9 @@ int authenticate_user_krb5pwd(request_rec *r, } sent_pw = ap_pbase64decode(r->pool, auth_line); - MK_USER = ap_getword (r->pool, &sent_pw, ':'); - MK_AUTH_TYPE = "Basic"; - + sent_name = ap_getword (r->pool, &sent_pw, ':'); /* do not allow user to override realm setting of server */ - if (strchr(MK_USER, '@')) { + if (strchr(sent_name, '@')) { log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "specifying realm in user name is prohibited"); ret = HTTP_UNAUTHORIZED; @@ -531,8 +621,8 @@ int authenticate_user_krb5pwd(request_rec *r, } if (conf->krb_5_keytab) + krb5_kt_resolve(kcontext, conf->krb_5_keytab, &keytab); /* setenv("KRB5_KTNAME", conf->krb_5_keytab, 1); */ - kcontext->default_keytab = conf->krb_5_keytab; realms = conf->krb_auth_realms; do { @@ -540,14 +630,16 @@ int authenticate_user_krb5pwd(request_rec *r, ap_getword_white(r->pool, &realms)))) continue; - code = krb5_parse_name(kcontext, MK_USER, &client); + if (client) { + krb5_free_principal(kcontext, client); + client = NULL; + } + code = krb5_parse_name(kcontext, sent_name, &client); if (code) continue; - code = krb5_verify_user(kcontext, client, ccache, sent_pw, 1, - (conf->service_name) ? conf->service_name : "khttp"); - krb5_free_principal(kcontext, client); - client = NULL; + code = verify_krb5_user(r, kcontext, client, ccache, sent_pw, "khttp", + keytab); if (code == 0) break; @@ -565,11 +657,19 @@ int authenticate_user_krb5pwd(request_rec *r, goto end; } - if (conf->krb_save_credentials) { - ret = store_krb5_creds(kcontext, r, conf, ccache); - if (ret) /* Ignore error ?? */ - goto end; + code = krb5_unparse_name(kcontext, client, &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; } + MK_USER = ap_pstrdup (r->pool, name); + MK_AUTH_TYPE = "Basic"; + free(name); + + if (conf->krb_save_credentials) + store_krb5_creds(kcontext, r, conf, ccache); ret = OK; @@ -578,6 +678,8 @@ end: krb5_free_principal(kcontext, client); if (ccache) krb5_cc_destroy(kcontext, ccache); + if (keytab) + krb5_kt_close(kcontext, keytab); krb5_free_context(kcontext); return ret; @@ -629,6 +731,8 @@ cleanup_gss_connection(void *data) if (gss_conn->server_creds != GSS_C_NO_CREDENTIAL) gss_release_cred(&minor_status, &gss_conn->server_creds); + gss_connection = NULL; + return OK; } @@ -692,18 +796,15 @@ get_gss_creds(request_rec *r, gss_buffer_desc input_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]; + + snprintf(buf, sizeof(buf), "%s/%s", "khttp", ap_get_server_name(r)); + + input_token.value = buf; + input_token.length = strlen(buf) + 1; - if (conf->service_name) { - input_token.value = conf->service_name; - input_token.length = strlen(conf->service_name) + 1; - } - else { - input_token.value = "khttp"; - input_token.length = 6; - } major_status = gss_import_name(&minor_status, &input_token, - (conf->service_name) ? - GSS_C_NT_USER_NAME : GSS_C_NT_HOSTBASED_SERVICE, + GSS_C_NT_USER_NAME, &server_name); if (GSS_ERROR(major_status)) { log_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -738,6 +839,12 @@ authenticate_user_gss(request_rec *r, int ret; gss_name_t client_name = GSS_C_NO_NAME; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; + static int initial_return = HTTP_UNAUTHORIZED; + + /* needed to work around replay caches */ + if (!ap_is_initial_req(r)) + return initial_return; + initial_return = HTTP_UNAUTHORIZED; if (gss_connection == NULL) { gss_connection = ap_pcalloc(r->connection->pool, sizeof(*gss_connection)); @@ -843,28 +950,6 @@ authenticate_user_gss(request_rec *r, gss_release_buffer(&minor_status, &output_token); - -#if 0 - /* If the user comes from a realm specified by configuration don't include - its realm name in the username so that the authorization routine could - work for both Password-based and Ticket-based authentication. It's - administrators responsibility to include only such realm that have - unified principal instances, i.e. if the same principal name occures in - multiple realms, it must be always assigned to a single user. - */ - p = strchr(r->connection->user, '@'); - if (p != NULL) { - const char *realms = conf->gss_krb5_realms; - - while (realms && *realms) { - if (strcmp(p+1, ap_getword_white(r->pool, &realms)) == 0) { - *p = '\0'; - break; - } - } - } -#endif - ret = OK; end: @@ -877,32 +962,37 @@ end: if (client_name != GSS_C_NO_NAME) gss_release_name(&minor_status, &client_name); + cleanup_gss_connection(gss_connection); + + initial_return = ret; return ret; } #endif /* KRB5 */ static void -note_kerb_auth_failure(request_rec *r, const kerb_auth_config *conf) +note_kerb_auth_failure(request_rec *r, const kerb_auth_config *conf, + int use_krb4, int use_krb5) { const char *auth_name = NULL; + int set_basic = 0; /* get the user realm specified in .htaccess */ auth_name = ap_auth_name(r); - /* XXX check AuthType */ - /* XXX should the WWW-Authenticate header be cleared first? */ #ifdef KRB5 - if (conf->krb_method_gssapi) + if (use_krb5 && conf->krb_method_gssapi) ap_table_add(r->err_headers_out, "WWW-Authenticate", "GSS-Negotiate "); - if (conf->krb_method_k5pass) + if (use_krb5 && conf->krb_method_k5pass) { ap_table_add(r->err_headers_out, "WWW-Authenticate", ap_pstrcat(r->pool, "Basic realm=\"", auth_name, "\"", NULL)); + set_basic = 1; + } #endif #ifdef KRB4 - if (conf->krb_method_k4pass) + if (use_krb4 && conf->krb_method_k4pass && !set_basic) ap_table_add(r->err_headers_out, "WWW-Authenticate", ap_pstrcat(r->pool, "Basic realm=\"", auth_name, "\"", NULL)); #endif @@ -926,7 +1016,7 @@ int kerb_authenticate_user(request_rec *r) use_krb5 = use_krb4 = 1; else if(type && strcasecmp(type, "KerberosV5") == 0) use_krb4 = 0; - else if (type && strcasecmp(type, "KerberosV4") == 0) + else if(type && strcasecmp(type, "KerberosV4") == 0) use_krb5 = 0; else return DECLINED; @@ -934,7 +1024,7 @@ int kerb_authenticate_user(request_rec *r) /* get what the user sent us in the HTTP header */ auth_line = MK_TABLE_GET(r->headers_in, "Authorization"); if (!auth_line) { - note_kerb_auth_failure(r, conf); + note_kerb_auth_failure(r, conf, use_krb4, use_krb5); return HTTP_UNAUTHORIZED; } auth_type = ap_getword_white(r->pool, &auth_line); @@ -958,7 +1048,7 @@ int kerb_authenticate_user(request_rec *r) #endif if (ret == HTTP_UNAUTHORIZED) - note_kerb_auth_failure(r, conf); + note_kerb_auth_failure(r, conf, use_krb4, use_krb5); return ret; }