#include <stdio.h>
#include <stdarg.h>
-#define MODAUTHKERB_VERSION "5.2"
+#define MODAUTHKERB_VERSION "5.3"
#define MECH_NEGOTIATE "Negotiate"
#define SERVICE_NAME "HTTP"
char *krb_5_keytab;
int krb_method_gssapi;
int krb_method_k5pass;
+ int krb5_do_auth_to_local;
#endif
#ifdef KRB4
char *krb_4_srvtab;
#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);
command("KrbMethodK5Passwd", ap_set_flag_slot, krb_method_k5pass,
FLAG, "Enable Kerberos V5 password authentication."),
+
+ command("KrbLocalUserMapping", ap_set_flag_slot, krb5_do_auth_to_local,
+ FLAG, "Set to 'on' to have Kerberos do auth_to_local mapping of principal names to system user names."),
#endif
#ifdef KRB4
};
#endif
-
/***************************************************************************
Auth Configuration Initialization
***************************************************************************/
((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)->krb_method_k5pass = 1;
((kerb_auth_config *)rec)->krb_method_gssapi = 1;
#endif
int all_principals_unkown;
sent_pw = ap_pbase64decode(r->pool, auth_line);
- sent_name = ap_getword (r->pool, &sent_pw, ':');
+ sent_name = ap_getword_nulls_nc (r->pool, (char **) &sent_pw, ':');
sent_instance = strchr(sent_name, '.');
if (sent_instance)
} else
keytab = ap_req_keytab;
+#ifdef HAVE_KRB5_CC_NEW_UNIQUE
+ ret = krb5_cc_new_unique(context, "MEMORY", NULL, &local_ccache);
+#else
ret = krb5_cc_resolve(context, "MEMORY:", &local_ccache);
+#endif
+
if (ret) {
log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"krb5_cc_resolve() failed when verifying KDC");
goto end;
}
+#ifdef HAVE_KRB5_CC_NEW_UNIQUE
+ ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ret_ccache);
+#else
ret = krb5_cc_resolve(context, "MEMORY:", &ret_ccache);
+#endif
+
if (ret) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"generating new memory ccache failed: %s",
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,
}
sent_pw = ap_pbase64decode(r->pool, auth_line);
- sent_name = ap_getword (r->pool, &sent_pw, ':');
+ sent_name = ap_getword_nulls_nc (r->pool, (char **) &sent_pw, ':');
if (sent_pw == NULL || *sent_pw == '\0') {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
********************************************************************/
static const char *
-get_gss_error(MK_POOL *p, OM_uint32 err_maj, OM_uint32 err_min, char *prefix)
+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;
- err_msg = apr_pstrdup(p, prefix);
+ log_rerror(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_NO_OID,
&msg_ctx,
&status_string);
- if (GSS_ERROR(maj_stat))
- break;
- err_msg = apr_pstrcat(p, err_msg, ": ", (char*) status_string.value, NULL);
- gss_release_buffer(&min_stat, &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);
+ }
+ } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+ msg_ctx = 0;
+ err_msg = apr_pstrcat(r->pool, err_msg, " (", NULL);
+ do {
maj_stat = gss_display_status (&min_stat,
err_min,
GSS_C_MECH_CODE,
&msg_ctx,
&status_string);
if (!GSS_ERROR(maj_stat)) {
- err_msg = apr_pstrcat(p, err_msg,
- " (", (char*) status_string.value, ")", NULL);
+ err_msg = apr_pstrcat(r->pool, err_msg, ", ",
+ (char *) status_string.value, NULL);
gss_release_buffer(&min_stat, &status_string);
}
} while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+ err_msg = apr_pstrcat(r->pool, err_msg, ")", NULL);
return err_msg;
}
if (GSS_ERROR(maj_stat)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Cannot store delegated credential (%s)",
- get_gss_error(r->pool, maj_stat, min_stat, "gss_krb5_copy_ccache"));
+ get_gss_error(r, maj_stat, min_stat, "gss_krb5_copy_ccache"));
goto end;
}
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 if (conf->krb_service_name && strcmp(conf->krb_service_name,"Any") == 0) {
+ *server_creds = GSS_C_NO_CREDENTIAL;
+ return 0;
+ }
else
snprintf(buf, sizeof(buf), "%s@%s",
(conf->krb_service_name) ? conf->krb_service_name : SERVICE_NAME,
memset(&token, 0, sizeof(token));
if (GSS_ERROR(major_status)) {
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, major_status, minor_status,
+ "%s", get_gss_error(r, major_status, minor_status,
"gss_import_name() failed"));
return HTTP_INTERNAL_SERVER_ERROR;
}
/* Perhaps we could just ignore this error but it's safer to give up now,
I think */
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, major_status, minor_status,
+ "%s", get_gss_error(r, major_status, minor_status,
"gss_display_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, major_status, minor_status,
+ "%s", get_gss_error(r, major_status, minor_status,
"gss_acquire_cred() failed"));
return HTTP_INTERNAL_SERVER_ERROR;
}
gss_OID_desc spnego_oid;
gss_ctx_id_t context = GSS_C_NO_CONTEXT;
gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
+ OM_uint32 ret_flags = 0;
*negotiate_ret_value = "\0";
&client_name,
NULL,
&output_token,
- NULL,
+ &ret_flags,
NULL,
&delegated_cred);
log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "Verification returned code %d", major_status);
+ "Client %s us their credential",
+ (ret_flags & GSS_C_DELEG_FLAG) ? "sent" : "didn't send");
if (output_token.length) {
char *token = NULL;
size_t len;
"Warning: received token seems to be NTLM, which isn't supported by the Kerberos module. Check your IE configuration.");
log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r->pool, major_status, minor_status,
+ "%s", get_gss_error(r, 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;
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, major_status, minor_status,
+ "%s", get_gss_error(r, major_status, minor_status,
"gss_display_name() failed"));
ret = HTTP_INTERNAL_SERVER_ERROR;
goto end;
if (conf->krb_save_credentials && delegated_cred != GSS_C_NO_CREDENTIAL)
store_gss_creds(r, conf, (char *)output_token.value, delegated_cred);
-
+
gss_release_buffer(&minor_status, &output_token);
ret = OK;
return ret;
}
-#endif /* KRB5 */
static int
-already_succeeded(request_rec *r)
+do_krb5_an_to_ln(request_rec *r) {
+ krb5_error_code code;
+ int ret = HTTP_INTERNAL_SERVER_ERROR;
+ char *MK_USER_LNAME = NULL;
+ krb5_context kcontext = NULL;
+ krb5_principal client = NULL;
+
+ code = krb5_init_context(&kcontext);
+ if (code) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Cannot initialize Kerberos5 context (%d)", code);
+ goto end;
+ }
+
+ code = krb5_parse_name(kcontext, MK_USER, &client);
+ if (code) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "krb5_parse_name() failed: %s",
+ krb5_get_err_text(kcontext, code));
+ goto end;
+ }
+ MK_USER_LNAME = apr_pcalloc(r->pool, strlen(MK_USER)+1);
+ if (MK_USER_LNAME == NULL) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "ap_pcalloc() failed (not enough memory)");
+ goto end;
+ }
+ code = krb5_aname_to_localname(kcontext, client, strlen(MK_USER), MK_USER_LNAME);
+ if (code) {
+ if (code != KRB5_LNAME_NOTRANS) {
+ log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "krb5_aname_to_localname() failed: %s",
+ krb5_get_err_text(kcontext, code));
+
+ }
+ else {
+ log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r,
+ "krb5_aname_to_localname() found no "
+ "mapping for principal %s",
+ MK_USER);
+ }
+ }
+ else {
+ log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "kerb_authenticate_a_name_to_local_name %s -> %s",
+ (MK_USER)?MK_USER:"(NULL)", (MK_USER_LNAME)?MK_USER_LNAME:"(NULL)");
+ MK_USER = apr_pstrdup(r->pool, MK_USER_LNAME);
+ ret = OK;
+ }
+ end:
+ if (client)
+ krb5_free_principal(kcontext, client);
+ if (kcontext)
+ krb5_free_context(kcontext);
+ return ret;
+}
+
+
+#endif /* KRB5 */
+
+static krb5_conn_data *
+already_succeeded(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;
+ const 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(&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
kerb_auth_config *conf =
(kerb_auth_config *) ap_get_module_config(r->per_dir_config,
&auth_kerb_module);
+ krb5_conn_data *prevauth = NULL;
const char *auth_type = NULL;
const char *auth_line = NULL;
const char *type = NULL;
int ret;
static int last_return = HTTP_UNAUTHORIZED;
char *negotiate_ret_value = NULL;
+ char keyname[1024];
/* get the type specified in .htaccess */
type = ap_auth_type(r);
(strcasecmp(auth_type, "Basic") == 0))
return DECLINED;
- if (already_succeeded(r))
- return last_return;
-
- ret = HTTP_UNAUTHORIZED;
+ if ( (prevauth = already_succeeded(r, auth_line)) == NULL) {
+ ret = HTTP_UNAUTHORIZED;
#ifdef KRB5
if (use_krb5 && conf->krb_method_gssapi &&
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) {
+ prevauth = (krb5_conn_data *) apr_pcalloc(r->connection->pool, sizeof(krb5_conn_data));
+ 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);
+ }
+
+ if (ret == OK && conf->krb5_do_auth_to_local)
+ ret = do_krb5_an_to_ln(r);
+
/* XXX log_debug: if ret==OK, log(user XY authenticated) */
last_return = ret;