#include <stdio.h>
#include <stdarg.h>
-#define MODAUTHKERB_VERSION "5.3"
+#define MODAUTHKERB_VERSION "5.4"
#define MECH_NEGOTIATE "Negotiate"
#define SERVICE_NAME "HTTP"
#define apr_pool_cleanup_register ap_register_cleanup
#endif /* STANDARD20_MODULE_STUFF */
+#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER== 2
+#define APACHE22
+#include "mod_auth.h"
+#endif
+
#ifdef _WIN32
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#include <netdb.h> /* gethostbyname() */
#endif /* KRB4 */
-#ifndef _WIN32
-/* should be HAVE_UNISTD_H instead */
+#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#endif
} kerb_auth_config;
+typedef struct krb5_conn_data {
+ char *authline;
+ char *user;
+ char *mech;
+ int last_return;
+} krb5_conn_data;
+
static void
set_kerb_auth_headers(request_rec *r, const kerb_auth_config *conf,
int use_krb4, int use_krb5pwd, char *negotiate_ret_value);
}
/* And this is the operations vector for our replay cache */
-const krb5_rc_ops_internal mod_auth_kerb_rc_ops = {
+static const krb5_rc_ops_internal mod_auth_kerb_rc_ops = {
0,
"dfl",
krb5_rc_dfl_init,
((kerb_auth_config *)rec)->krb_ssl_preauthentication = 0;
#endif
#ifdef KRB5
- ((kerb_auth_config *)rec)->krb5_do_auth_to_local = 0;
+ ((kerb_auth_config *)rec)->krb5_do_auth_to_local = 0;
((kerb_auth_config *)rec)->krb_method_k5pass = 1;
((kerb_auth_config *)rec)->krb_method_gssapi = 1;
#endif
const char *auth_line)
{
int ret;
- const char *sent_pw;
+ char *sent_pw;
const char *sent_name;
char *sent_instance;
char tkt_file[32];
user = apr_pstrcat(r->pool, user, "@", realm, NULL);
MK_USER = user;
- MK_AUTH_TYPE = "Basic";
+ MK_AUTH_TYPE = "Kerberos";
apr_table_setn(r->subprocess_env, "KRBTKFILE", tkt_file_p);
if (!conf->krb_save_credentials)
static krb5_error_code
verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal,
const char *password, krb5_principal server,
- krb5_keytab keytab, int krb_verify_kdc, krb5_ccache *ccache)
+ krb5_keytab keytab, int krb_verify_kdc,
+ const char *krb_service_name, krb5_ccache *ccache)
{
krb5_creds creds;
+ krb5_get_init_creds_opt options;
krb5_error_code ret;
krb5_ccache ret_ccache = NULL;
char *name = NULL;
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
/* XXX error messages shouldn't be logged here (and in the while() loop in
* authenticate_user_krb5pwd() as weell), in order to avoid confusing log
free(name);
}
+ krb5_get_init_creds_opt_init(&options);
ret = krb5_get_init_creds_password(context, &creds, principal,
(char *)password, NULL,
- NULL, 0, NULL, NULL);
+ NULL, 0, NULL, &options);
if (ret) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"krb5_get_init_creds_password() failed: %s",
}
*/
- if (krb_verify_kdc &&
+ /*if (krb_verify_kdc &&
(ret = verify_krb5_init_creds(r, context, &creds, server, keytab))) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"failed to verify krb5 credentials: %s",
krb5_get_err_text(context, ret));
goto end;
+ }*/
+
+ if (krb_verify_kdc) {
+ if (krb_service_name && strcmp(krb_service_name,"Any") == 0) {
+ ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if(!ret) {
+ while((krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
+ if ((ret = verify_krb5_init_creds(r, context, &creds, entry.principal, keytab)) == 0)
+ break;
+ }
+ }
+ if (ret) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "failed to verify krb5 credentials: %s",
+ krb5_get_err_text(context, ret));
+ krb5_kt_end_seq_get(context, keytab, &cursor);
+ krb5_kt_close(context, keytab);
+ goto end;
+ }
+ krb5_kt_end_seq_get(context, keytab, &cursor);
+ krb5_kt_close(context, keytab);
+ }
+ else {
+ if ((ret = verify_krb5_init_creds(r, context, &creds, server, keytab))) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "failed to verify krb5 credentials: %s",
+ krb5_get_err_text(context, ret));
+ goto end;
+ }
+ }
}
#ifdef HAVE_KRB5_CC_NEW_UNIQUE
return OK;
}
-
static int
authenticate_user_krb5pwd(request_rec *r,
kerb_auth_config *conf,
int all_principals_unkown;
char *p = NULL;
- //temporary fix for KrbServiceName Any, use default SERVICE_NAME
- if (conf->krb_service_name && strcmp(conf->krb_service_name,"Any") == 0)
- snprintf(conf->krb_service_name, 5,"%s",SERVICE_NAME);
-
code = krb5_init_context(&kcontext);
if (code) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
}
code = verify_krb5_user(r, kcontext, client, sent_pw,
- server, keytab, conf->krb_verify_kdc, &ccache);
+ server, keytab, conf->krb_verify_kdc, conf->krb_service_name, &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 */
goto end;
}
MK_USER = apr_pstrdup (r->pool, name);
- MK_AUTH_TYPE = "Basic";
+ MK_AUTH_TYPE = "Kerberos";
free(name);
if (conf->krb_save_credentials)
token.length = strlen(buf) + 1;
major_status = gss_import_name(&minor_status, &token,
- (have_server_princ) ? GSS_KRB5_NT_PRINCIPAL_NAME : GSS_C_NT_HOSTBASED_SERVICE,
+ (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)) {
return 0;
}
+#ifndef GSSAPI_SUPPORTS_SPNEGO
static int
cmp_gss_type(gss_buffer_t token, gss_OID oid)
{
return memcmp(p, oid->elements, oid->length);
}
+#endif
static int
authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
gss_accept_sec_context_spnego : gss_accept_sec_context;
#endif
- log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Verifying client data using %s",
+ log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Verifying client data using KRB5 GSS-API %s",
(accept_sec_token == gss_accept_sec_context)
- ? "KRB5 GSS-API"
- : "SPNEGO GSS-API");
+ ? ""
+ : "with our SPNEGO lib");
major_status = accept_sec_token(&minor_status,
&context,
&delegated_cred);
log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"Client %s us their credential",
- (ret_flags & GSS_C_DELEG_FLAG) ? "sent" : "didn't send");
+ (ret_flags & GSS_C_DELEG_FLAG) ? "delegated" : "didn't delegate");
if (output_token.length) {
char *token = NULL;
size_t len;
goto end;
}
-#if 0
- /* This is a _Kerberos_ module so multiple authentication rounds aren't
- * supported. If we wanted a generic GSS authentication we would have to do
- * some magic with exporting context etc. */
+ /* Multiple authentication rounds aren't supported. If we wanted a generic
+ * GSS authentication we would have to do some magic with exporting context
+ * etc. */
if (major_status & GSS_S_CONTINUE_NEEDED) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Multi-iteration authentication isn't supported");
ret = HTTP_UNAUTHORIZED;
goto end;
}
-#endif
major_status = gss_display_name(&minor_status, client_name, &output_token, NULL);
gss_release_name(&minor_status, &client_name);
#endif /* KRB5 */
-static int
-already_succeeded(request_rec *r)
+static krb5_conn_data *
+already_authorized(request_rec *r, char *auth_line)
{
- if (ap_is_initial_req(r) || MK_AUTH_TYPE == NULL)
- return 0;
- if (strcmp(MK_AUTH_TYPE, MECH_NEGOTIATE) ||
- (strcmp(MK_AUTH_TYPE, "Basic") && strchr(MK_USER, '@')))
- return 1;
- return 0;
+ krb5_conn_data *conn_data;
+ char keyname[1024];
+
+ snprintf(keyname, sizeof(keyname) - 1,
+ "mod_auth_kerb::connection::%s::%ld", r->connection->remote_ip,
+ r->connection->id);
+
+ if (apr_pool_userdata_get((void**)&conn_data, keyname, r->connection->pool) != 0)
+ return NULL;
+
+ if(conn_data) {
+ if(strcmp(conn_data->authline, auth_line) == 0) {
+ log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "matched previous auth request");
+ return conn_data;
+ }
+ }
+ return NULL;
+}
+
+static void
+save_authorized(request_rec *r, char *auth_line, const char *auth_type, int ret) {
+ krb5_conn_data *prevauth;
+ prevauth = (krb5_conn_data *)apr_palloc(r->connection->pool, sizeof(krb5_conn_data));
+
+ char keyname[1024];
+
+ prevauth->user = apr_pstrdup(r->connection->pool, MK_USER);
+ prevauth->authline = apr_pstrdup(r->connection->pool, auth_line);
+ prevauth->mech = apr_pstrdup(r->connection->pool, auth_type);
+ prevauth->last_return = ret;
+
+ snprintf(keyname, sizeof(keyname) - 1,
+ "mod_auth_kerb::connection::%s::%ld",
+ r->connection->remote_ip, r->connection->id);
+ apr_pool_userdata_set(prevauth, keyname, NULL, r->connection->pool);
}
static void
}
static int
+authenticate_user(request_rec *r, char *auth_line, const char *type, int use_krb4, int use_krb5)
+{
+ int ret;
+ krb5_conn_data *prevauth = NULL;
+ kerb_auth_config *conf =
+ (kerb_auth_config *) ap_get_module_config(r->per_dir_config,
+ &auth_kerb_module);
+ char *negotiate_ret_value = NULL;
+ const char *auth_type = NULL;
+
+ if (!auth_line) {
+ set_kerb_auth_headers(r, conf, use_krb4, use_krb5,
+ (use_krb5) ? "\0" : NULL);
+ return HTTP_UNAUTHORIZED;
+ }
+ auth_type = ap_getword_white(r->pool, (const char **)&auth_line);
+
+ /* If we are delegating Basic to other modules, DECLINE the request */
+ if (conf->krb_delegate_basic &&
+#ifdef KRB5
+ !conf->krb_method_k5pass &&
+#endif
+#ifdef KRB4
+ !conf->krb_method_k4pass &&
+#endif
+ (strcasecmp(auth_type, "Basic") == 0))
+ return DECLINED;
+ if ((prevauth = already_authorized(r, auth_line)) == NULL) {
+ ret = HTTP_UNAUTHORIZED;
+
+#ifdef KRB5
+ if (use_krb5 && conf->krb_method_gssapi &&
+ strcasecmp(auth_type, MECH_NEGOTIATE) == 0) {
+ ret = authenticate_user_gss(r, conf, auth_line, &negotiate_ret_value);
+ } else if (use_krb5 && (conf->krb_method_k5pass || strcasecmp(type, "Basic"))){
+ ret = authenticate_user_krb5pwd(r, conf, auth_line);
+ }
+#endif
+
+#ifdef KRB4
+ if (ret == HTTP_UNAUTHORIZED && use_krb4 && (conf->krb_method_k4pass || strcasecmp(type, "Basic")))
+ ret = authenticate_user_krb4pwd(r, conf, auth_line);
+#endif
+
+ if (ret == HTTP_UNAUTHORIZED)
+ set_kerb_auth_headers(r, conf, use_krb4, use_krb5, negotiate_ret_value);
+
+ } else {
+ ret = prevauth->last_return;
+ MK_USER = prevauth->user;
+ MK_AUTH_TYPE = prevauth->mech;
+ }
+
+ /*
+ * save who was auth'd, if it's not already stashed.
+ */
+ if(!prevauth) {
+ save_authorized(r, auth_line, auth_type, ret);
+ }
+
+ if (ret == OK && conf->krb5_do_auth_to_local) {
+ ret = do_krb5_an_to_ln(r);
+ }
+ return ret;
+}
+
+static authn_status authn_krb_password(request_rec *r, const char *user,
+ const char *password)
+{
+ char *auth_line = NULL;
+ int ret;
+ const char *type = NULL;
+
+ type = ap_auth_type(r);
+ auth_line = ap_pbase64encode (r->pool, apr_psprintf(r->pool, "%s:%s", user, password));
+ auth_line = apr_psprintf(r->pool, "Basic %s", auth_line);
+
+ ret = authenticate_user(r, auth_line, type, 1, 1);
+
+ if (ret == OK) return AUTH_GRANTED;
+ else return AUTH_USER_NOT_FOUND;
+}
+
+static int
kerb_authenticate_user(request_rec *r)
{
kerb_auth_config *conf =
(kerb_auth_config *) ap_get_module_config(r->per_dir_config,
&auth_kerb_module);
- const char *auth_type = NULL;
- const char *auth_line = NULL;
+ char *auth_line = NULL;
+ int ret, use_krb4 = 0, use_krb5 = 0;
const char *type = NULL;
- int use_krb5 = 0, use_krb4 = 0;
- int ret;
- static int last_return = HTTP_UNAUTHORIZED;
- char *negotiate_ret_value = NULL;
-
+
/* get the type specified in .htaccess */
type = ap_auth_type(r);
#endif
/* get what the user sent us in the HTTP header */
- auth_line = MK_TABLE_GET(r->headers_in, (r->proxyreq == PROXYREQ_PROXY)
+ auth_line = (char *)MK_TABLE_GET(r->headers_in, (r->proxyreq == PROXYREQ_PROXY)
? "Proxy-Authorization"
: "Authorization");
- if (!auth_line) {
- set_kerb_auth_headers(r, conf, use_krb4, use_krb5,
- (use_krb5) ? "\0" : NULL);
- return HTTP_UNAUTHORIZED;
- }
- auth_type = ap_getword_white(r->pool, &auth_line);
+ ret = authenticate_user(r, auth_line, type, use_krb4, use_krb5);
- /* If we are delegating Basic to other modules, DECLINE the request */
- if (conf->krb_delegate_basic &&
-#ifdef KRB5
- !conf->krb_method_k5pass &&
-#endif
-#ifdef KRB4
- !conf->krb_method_k4pass &&
-#endif
- (strcasecmp(auth_type, "Basic") == 0))
- return DECLINED;
-
- if (already_succeeded(r))
- return last_return;
-
- ret = HTTP_UNAUTHORIZED;
-
-#ifdef KRB5
- if (use_krb5 && conf->krb_method_gssapi &&
- strcasecmp(auth_type, MECH_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);
- }
- if (ret == OK && conf->krb5_do_auth_to_local)
- ret = do_krb5_an_to_ln(r);
-#endif
-
-#ifdef KRB4
- if (ret == HTTP_UNAUTHORIZED && use_krb4 && conf->krb_method_k4pass &&
- strcasecmp(auth_type, "Basic") == 0)
- ret = authenticate_user_krb4pwd(r, conf, auth_line);
-#endif
-
- if (ret == HTTP_UNAUTHORIZED)
- set_kerb_auth_headers(r, conf, use_krb4, use_krb5, negotiate_ret_value);
-
- /* XXX log_debug: if ret==OK, log(user XY authenticated) */
-
- last_return = ret;
return ret;
}
-int
+static int
have_rcache_type(const char *type)
{
krb5_error_code ret;
static void
kerb_register_hooks(apr_pool_t *p)
{
+#ifdef APACHE22
+ static const authn_provider authn_krb_provider = {
+ &authn_krb_password,
+ NULL
+ };
+
+ ap_register_provider(p, AUTHN_PROVIDER_GROUP, "kerberos", "0", &authn_krb_provider);
+#endif
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);
}