/*
- * Daniel Kouril <kouril@users.sourceforge.org>
+ * Daniel Kouril <kouril@users.sourceforge.net>
*
* Source and Documentation can be found at:
* http://modauthkerb.sourceforge.net/
* Based on work by
* James E. Robinson, III <james@ncstate.net>
* Daniel Henninger <daniel@ncsu.edu>
+ * Ludek Sulak <xsulak@fi.muni.cz>
*/
/* ====================================================================
#ident "$Id$"
+#include "config.h"
+
+#define MODAUTHKERB_VERSION "5.0-rc4"
+
#ifndef APXS1
#include "ap_compat.h"
#include "apr_strings.h"
# define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
# define krb5_get_err_text(context,code) error_message(code)
#endif
+#include "spnegokrb5.h"
#endif /* KRB5 */
#ifdef KRB4
# undef closesocket
#endif
#include <krb.h>
+#include <netdb.h> /* gethostbyname() */
#endif /* KRB4 */
#ifdef APXS1
#ifdef APXS1
#define MK_POOL pool
#define MK_TABLE_GET ap_table_get
-#define MK_TABLE_SET ap_table_set
-#define MK_TABLE_TYPE table
-#define MK_PSTRDUP ap_pstrdup
#define MK_USER r->connection->user
#define MK_AUTH_TYPE r->connection->ap_auth_type
-#define MK_ARRAY_HEADER array_header
#else
#define MK_POOL apr_pool_t
#define MK_TABLE_GET apr_table_get
-#define MK_TABLE_SET apr_table_set
-#define MK_TABLE_TYPE apr_table_t
-#define MK_PSTRDUP apr_pstrdup
#define MK_USER r->user
#define MK_AUTH_TYPE r->ap_auth_type
-#define MK_ARRAY_HEADER apr_array_header_t
#endif /* APXS1 */
typedef struct {
char *krb_auth_realms;
int krb_save_credentials;
+ int krb_verify_kdc;
+ char *krb_service_name;
+ int krb_authoritative;
#ifdef KRB5
char *krb_5_keytab;
int krb_method_gssapi;
command("KrbSaveCredentials", ap_set_flag_slot, krb_save_credentials,
FLAG, "Save and store credentials/tickets retrieved during auth."),
+ command("KrbVerifyKDC", ap_set_flag_slot, krb_verify_kdc,
+ 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."),
+
+ command("KrbAuthoritative", ap_set_flag_slot, krb_authoritative,
+ FLAG, "Set to 'off' to allow access control to be passed along to lower modules if the UserID is not known to this module."),
+
#ifdef KRB5
command("Krb5Keytab", ap_set_file_slot, krb_5_keytab,
TAKE1, "Location of Kerberos V5 keytab file."),
command("KrbMethodNegotiate", ap_set_flag_slot, krb_method_gssapi,
FLAG, "Enable Negotiate authentication method."),
- command("KrbMethodK5Pass", ap_set_flag_slot, krb_method_k5pass,
+ command("KrbMethodK5Passwd", ap_set_flag_slot, krb_method_k5pass,
FLAG, "Enable Kerberos V5 password authentication."),
#endif
command("Krb4Srvtab", ap_set_file_slot, krb_4_srvtab,
TAKE1, "Location of Kerberos V4 srvtab file."),
- command("KrbMethodK4Pass", ap_set_flag_slot, krb_method_k4pass,
+ command("KrbMethodK4Passwd", ap_set_flag_slot, krb_method_k4pass,
FLAG, "Enable Kerberos V4 password authentication."),
#endif
} gss_connection_t;
static gss_connection_t *gss_connection = NULL;
+
+static const char *EMPTY_STRING = "\0";
#endif
kerb_auth_config *rec;
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_authoritative = 1;
#ifdef KRB5
((kerb_auth_config *)rec)->krb_method_k5pass = 1;
((kerb_auth_config *)rec)->krb_method_gssapi = 1;
vsnprintf(errstr, sizeof(errstr), fmt, ap);
va_end(ap);
+
#ifdef APXS1
- ap_log_rerror(file, line, level, r, "%s", errstr);
+ ap_log_rerror(file, line, level | APLOG_NOERRNO, r, "%s", errstr);
#else
- ap_log_rerror(file, line, level, status, r, "%s", errstr);
+ ap_log_rerror(file, line, level | APLOG_NOERRNO, status, r, "%s", errstr);
#endif
}
***************************************************************************/
static int
verify_krb4_user(request_rec *r, char *name, char *instance, char *realm,
- char *password, char *linstance, char *srvtab)
+ char *password, char *linstance, char *srvtab, int krb_verify_kdc)
{
int ret;
char *phost;
ret = krb_get_pw_in_tkt(name, instance, realm, "krbtgt", realm,
DEFAULT_TKT_LIFE, password);
- if (ret)
- /* log(krb_err_txt[ret]) */
+ if (ret) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Cannot get krb4 ticket: krb_get_pw_in_tkt() failed: %s",
+ krb_get_err_text(ret));
+ return ret;
+ }
+
+ if (!krb_verify_kdc)
return ret;
hostname = ap_get_server_name(r);
hp = gethostbyname(hostname);
if (hp == NULL) {
dest_tkt();
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Cannot verify krb4 ticket: gethostbyname() failed: %s",
+ hstrerror(h_errno));
return h_errno;
}
memcpy(&addr, hp->h_addr, sizeof(addr));
ret = krb_mk_req(&ticket, linstance, phost, lrealm, 0);
if (ret) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Cannot verify krb4 ticket: krb_mk_req() failed: %s",
+ krb_get_err_text(ret));
dest_tkt();
return ret;
}
ret = krb_rd_req(&ticket, linstance, phost, addr, &authdata, srvtab);
- if (ret)
+ if (ret) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Cannot verify krb4 ticket: krb_rd_req() failed: %s",
+ krb_get_err_text(ret));
dest_tkt();
+ }
return ret;
}
const char *realm;
char *user;
char lrealm[REALM_SZ];
+ int all_principals_unkown;
sent_pw = ap_pbase64decode(r->pool, auth_line);
sent_name = ap_getword (r->pool, &sent_pw, ':');
krb_set_tkt_string(tkt_file);
+ all_principals_unkown = 1;
realms = conf->krb_auth_realms;
do {
memset(lrealm, 0, sizeof(lrealm));
if (realm == NULL) {
ret = krb_get_lrealm(lrealm, 1);
+ if (ret)
+ break;
realm = lrealm;
}
- if (realm == NULL || *realm == '\0')
- break;
- ret = verify_krb4_user(r, (char *)sent_name, sent_instance,
- (char *)realm, (char *)sent_pw, "khttp",
- conf->krb_4_srvtab);
+ ret = verify_krb4_user(r, (char *)sent_name,
+ (sent_instance) ? sent_instance : "",
+ (char *)realm, (char *)sent_pw,
+ conf->krb_service_name,
+ conf->krb_4_srvtab, conf->krb_verify_kdc);
+ if (!conf->krb_authoritative && ret) {
+ /* 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 */
+ if (all_principals_unkown && ret != KDC_PR_UNKNOWN)
+ all_principals_unkown = 0;
+ }
+
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;
+ /* XXX log only in the verify_krb4_user() call */
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Verifying krb4 password failed");
+ ret = (!conf->krb_authoritative && all_principals_unkown == 1 && ret == KDC_PR_UNKNOWN) ?
+ DECLINED : HTTP_UNAUTHORIZED;
goto end;
}
user = ap_pstrdup(r->pool, sent_name);
if (sent_instance)
- user = ap_pstrcat(r->pool, ".", sent_instance);
- user = ap_pstrcat(r->pool, "@", realm);
+ user = ap_pstrcat(r->pool, user, ".", sent_instance, NULL);
+ user = ap_pstrcat(r->pool, user, "@", realm, NULL);
MK_USER = user;
MK_AUTH_TYPE = "Basic";
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)
+ krb5_keytab keytab, int krb_verify_kdc)
{
krb5_creds creds;
krb5_principal server = NULL;
goto end;
krb5_verify_init_creds_opt_init(&opt);
- krb5_verify_init_creds_opt_set_ap_req_nofail(&opt, 1);
+ krb5_verify_init_creds_opt_set_ap_req_nofail(&opt, krb_verify_kdc);
ret = krb5_verify_init_creds(context, &creds, server, keytab, NULL, &opt);
if (ret)
krb5_ccache *ccache)
{
char *ccname;
+ int fd;
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) {
+ ccname = ap_psprintf(r->pool, "FILE:%s/krb5cc_apache_XXXXXX", P_tmpdir);
+ fd = mkstemp(ccname + strlen("FILE:"));
+ if (fd < 0) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Cannot create file for new krb5 ccache: %s",
- krb5_get_err_text(kcontext, problem));
+ "mkstemp() failed: %s", strerror(errno));
ret = HTTP_INTERNAL_SERVER_ERROR;
goto end;
}
+ close(fd);
- ccname = ap_pstrdup(r->pool, krb5_cc_get_name(kcontext, tmp_ccache));
+ problem = krb5_cc_resolve(kcontext, ccname, &tmp_ccache);
+ if (problem) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "krb5_cc_resolve() failed: %s",
+ krb5_get_err_text(kcontext, problem));
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ unlink(ccname);
+ goto end;
+ }
problem = krb5_cc_initialize(kcontext, tmp_ccache, princ);
if (problem) {
krb5_ccache ccache = NULL;
krb5_keytab keytab = NULL;
int ret;
- char *name = NULL;
+ char *name = NULL;
+ int all_principals_unkown;
+ char *ccname = NULL;
+ int fd;
code = krb5_init_context(&kcontext);
if (code) {
"specifying realm in user name is prohibited");
ret = HTTP_UNAUTHORIZED;
goto end;
- }
+ }
-#ifdef HEIMDAL
- code = krb5_cc_gen_new(kcontext, &krb5_mcc_ops, &ccache);
-#else
- code = krb5_mcc_generate_new(kcontext, &ccache);
-#endif
+ /* XXX Heimdal allows to use the MEMORY: type with empty argument ? */
+ ccname = ap_psprintf(r->pool, "MEMORY:%s/krb5cc_apache_XXXXXX", P_tmpdir);
+ fd = mkstemp(ccname + strlen("MEMORY:"));
+ if (fd < 0) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "mkstemp() failed: %s", strerror(errno));
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+ close(fd);
+
+ code = krb5_cc_resolve(kcontext, ccname, &ccache);
if (code) {
- log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Cannot generate new ccache: %s",
- krb5_get_err_text(kcontext, code));
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "krb5_cc_resolve() failed: %s",
+ krb5_get_err_text(kcontext, code));
ret = HTTP_INTERNAL_SERVER_ERROR;
+ unlink(ccname);
goto end;
}
if (conf->krb_5_keytab)
krb5_kt_resolve(kcontext, conf->krb_5_keytab, &keytab);
- /* setenv("KRB5_KTNAME", conf->krb_5_keytab, 1); */
+ all_principals_unkown = 1;
realms = conf->krb_auth_realms;
do {
if (realms && (code = krb5_set_default_realm(kcontext,
if (code)
continue;
- code = verify_krb5_user(r, kcontext, client, ccache, sent_pw, "khttp",
- keytab);
+ code = verify_krb5_user(r, kcontext, client, ccache, sent_pw,
+ conf->krb_service_name,
+ keytab, conf->krb_verify_kdc);
+ 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 */
+ if (all_principals_unkown && code != KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN)
+ all_principals_unkown = 0;
+ }
+
if (code == 0)
break;
memset((char *)sent_pw, 0, strlen(sent_pw));
if (code) {
+ /* XXX log only in the verify_krb5_user() call */
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Verifying krb5 password failed: %s",
krb5_get_err_text(kcontext, code));
- ret = HTTP_UNAUTHORIZED;
+ if (!conf->krb_authoritative && all_principals_unkown == 1 && code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN)
+ ret = DECLINED;
+ else
+ ret = HTTP_UNAUTHORIZED;
+
goto end;
}
********************************************************************/
static const char *
-get_gss_error(MK_POOL *p, OM_uint32 error_status, char *prefix)
+get_gss_error(MK_POOL *p, OM_uint32 err_maj, OM_uint32 err_min, char *prefix)
{
- OM_uint32 maj_stat, min_stat;
+ OM_uint32 maj_stat, min_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string;
- char buf[1024];
+ char *err_msg;
size_t len;
- snprintf(buf, sizeof(buf), "%s", prefix);
- len = strlen(buf);
+ err_msg = ap_pstrdup(p, prefix);
do {
maj_stat = gss_display_status (&min_stat,
- error_status,
- GSS_C_MECH_CODE,
+ err_maj,
+ GSS_C_GSS_CODE,
GSS_C_NO_OID,
&msg_ctx,
&status_string);
- if (sizeof(buf) > len + status_string.length + 1) {
- sprintf(buf+len, ": %s", (char*) status_string.value);
- len += status_string.length;
- }
+ if (GSS_ERROR(maj_stat))
+ break;
+ err_msg = ap_pstrcat(p, err_msg, ": ", (char*) status_string.value, NULL);
gss_release_buffer(&min_stat, &status_string);
+
+ 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 = ap_pstrcat(p, err_msg,
+ " (", (char*) status_string.value, ")", NULL);
+ gss_release_buffer(&min_stat, &status_string);
+ }
} while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
- return (ap_pstrdup(p, buf));
+ return err_msg;
}
static int
if (GSS_ERROR(maj_stat)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Cannot store delegated credential (%s)",
- get_gss_error(r->pool, min_stat, "gss_krb5_copy_ccache"));
+ get_gss_error(r->pool, maj_stat, min_stat, "gss_krb5_copy_ccache"));
goto end;
}
gss_name_t server_name = GSS_C_NO_NAME;
char buf[1024];
- snprintf(buf, sizeof(buf), "%s/%s", "khttp", ap_get_server_name(r));
+ snprintf(buf, sizeof(buf), "%s/%s", conf->krb_service_name, ap_get_server_name(r));
input_token.value = buf;
input_token.length = strlen(buf) + 1;
&server_name);
if (GSS_ERROR(major_status)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, minor_status,
+ "%s", get_gss_error(r->pool, major_status, minor_status,
"gss_import_name() failed"));
return HTTP_INTERNAL_SERVER_ERROR;
}
gss_release_name(&minor_status2, &server_name);
if (GSS_ERROR(major_status)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, minor_status,
+ "%s", get_gss_error(r->pool, major_status, minor_status,
"gss_acquire_cred() failed"));
return HTTP_INTERNAL_SERVER_ERROR;
}
}
static int
-authenticate_user_gss(request_rec *r,
- kerb_auth_config *conf,
- const char *auth_line)
+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);
+}
+
+static int
+authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
+ 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;
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;
+ OM_uint32 (*accept_sec_token)();
+ gss_OID_desc spnego_oid;
- /* needed to work around replay caches */
- if (!ap_is_initial_req(r))
- return initial_return;
- initial_return = HTTP_UNAUTHORIZED;
+ *negotiate_ret_value = (char *)EMPTY_STRING;
+
+ spnego_oid.length = 6;
+ spnego_oid.elements = (void *)"\x2b\x06\x01\x05\x05\x02";
if (gss_connection == NULL) {
gss_connection = ap_pcalloc(r->connection->pool, sizeof(*gss_connection));
ap_register_cleanup(r->connection->pool, gss_connection, cleanup_gss_connection, ap_null_cleanup);
}
- if (conf->krb_5_keytab)
- setenv("KRB5_KTNAME", conf->krb_5_keytab, 1);
+ if (conf->krb_5_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->krb_5_keytab) + 1);
+ if (ktname == NULL) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "malloc() failed: not enough memory");
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+ sprintf(ktname, "KRB5_KTNAME=%s", conf->krb_5_keytab);
+ putenv(ktname);
+ }
if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
ret = get_gss_creds(r, conf, &gss_connection->server_creds);
}
input_token.length = ap_base64decode(input_token.value, auth_param);
- major_status = gss_accept_sec_context(&minor_status,
- &gss_connection->context,
- gss_connection->server_creds,
- &input_token,
- GSS_C_NO_CHANNEL_BINDINGS,
- &client_name,
- NULL,
- &output_token,
- NULL,
- NULL,
- &delegated_cred);
+ accept_sec_token = (cmp_gss_type(&input_token, &spnego_oid) == 0) ?
+ gss_accept_sec_context_spnego : gss_accept_sec_context;
+
+ major_status = accept_sec_token(&minor_status,
+ &gss_connection->context,
+ gss_connection->server_creds,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client_name,
+ NULL,
+ &output_token,
+ NULL,
+ NULL,
+ &delegated_cred);
if (output_token.length) {
char *token = NULL;
size_t len;
}
ap_base64encode(token, output_token.value, output_token.length);
token[len] = '\0';
- ap_table_set(r->err_headers_out, "WWW-Authenticate",
- ap_pstrcat(r->pool, "GSS-Negotiate ", token, NULL));
+ *negotiate_ret_value = token;
gss_release_buffer(&minor_status2, &output_token);
}
if (GSS_ERROR(major_status)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, minor_status,
+ "%s", get_gss_error(r->pool, major_status, minor_status,
"gss_accept_sec_context() failed"));
+ /* Don't offer the Negotiate method again if call to GSS layer failed */
+ *negotiate_ret_value = NULL;
ret = HTTP_UNAUTHORIZED;
goto end;
}
gss_release_name(&minor_status, &client_name);
if (GSS_ERROR(major_status)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, minor_status,
+ "%s", get_gss_error(r->pool, major_status, minor_status,
"gss_export_name() failed"));
ret = HTTP_INTERNAL_SERVER_ERROR;
goto end;
cleanup_gss_connection(gss_connection);
- initial_return = ret;
return ret;
}
#endif /* KRB5 */
+static int
+already_succeeded(request_rec *r)
+{
+ if (ap_is_initial_req(r) || MK_AUTH_TYPE == NULL)
+ return 0;
+ if (strcmp(MK_AUTH_TYPE, "Negotiate") ||
+ (strcmp(MK_AUTH_TYPE, "Basic") && strchr(MK_USER, '@')))
+ return 1;
+ return 0;
+}
static void
note_kerb_auth_failure(request_rec *r, const kerb_auth_config *conf,
- int use_krb4, int use_krb5)
+ int use_krb4, int use_krb5, char *negotiate_ret_value)
{
const char *auth_name = NULL;
int set_basic = 0;
+ char *negoauth_param;
/* get the user realm specified in .htaccess */
auth_name = ap_auth_name(r);
/* XXX should the WWW-Authenticate header be cleared first? */
#ifdef KRB5
- if (use_krb5 && conf->krb_method_gssapi)
- ap_table_add(r->err_headers_out, "WWW-Authenticate", "GSS-Negotiate ");
+ if (use_krb5 && conf->krb_method_gssapi && negotiate_ret_value != NULL) {
+ negoauth_param = (*negotiate_ret_value == '\0') ? "Negotiate" :
+ ap_pstrcat(r->pool, "Negotiate ", negotiate_ret_value, NULL);
+ ap_table_add(r->err_headers_out, "WWW-Authenticate", negoauth_param);
+ }
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));
const char *type = NULL;
int use_krb5 = 0, use_krb4 = 0;
int ret;
+ static int last_return = HTTP_UNAUTHORIZED;
+ char *negotiate_ret_value;
/* get the type specified in .htaccess */
type = ap_auth_type(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, use_krb4, use_krb5);
+ note_kerb_auth_failure(r, conf, use_krb4, use_krb5, "\0");
return HTTP_UNAUTHORIZED;
}
auth_type = ap_getword_white(r->pool, &auth_line);
+ if (already_succeeded(r))
+ return last_return;
+
ret = HTTP_UNAUTHORIZED;
#ifdef KRB5
if (use_krb5 && conf->krb_method_gssapi &&
- strcasecmp(auth_type, "GSS-Negotiate") == 0) {
- ret = authenticate_user_gss(r, conf, auth_line);
+ strcasecmp(auth_type, "Negotiate") == 0) {
+ ret = authenticate_user_gss(r, conf, auth_line, &negotiate_ret_value);
} else if (use_krb5 && conf->krb_method_k5pass &&
strcasecmp(auth_type, "Basic") == 0) {
ret = authenticate_user_krb5pwd(r, conf, auth_line);
#endif
if (ret == HTTP_UNAUTHORIZED)
- note_kerb_auth_failure(r, conf, use_krb4, use_krb5);
+ note_kerb_auth_failure(r, conf, use_krb4, use_krb5, negotiate_ret_value);
+ last_return = ret;
return ret;
}
NULL /* [ 1] post read_request handling */
};
#else
+static int
+kerb_init_handler(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ ap_add_version_component(p, "mod_auth_kerb/" MODAUTHKERB_VERSION);
+ return OK;
+}
+
void kerb_register_hooks(apr_pool_t *p)
{
+ ap_hook_post_config(kerb_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_check_user_id(kerb_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
}