X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmod_auth_kerb.c;h=13f7ad347551a2463a0fc658ad96cb7bf8b12935;hb=b07f536af4cd3a3f757e5e70e31051b0bb83cf17;hp=1f2cbaeab78adf571b7ce9c179246f1beccba513;hpb=90024fb09d04ccf4d7068f4ab41886829f29c906;p=mod_auth_kerb.cvs%2F.git diff --git a/src/mod_auth_kerb.c b/src/mod_auth_kerb.c index 1f2cbae..13f7ad3 100644 --- a/src/mod_auth_kerb.c +++ b/src/mod_auth_kerb.c @@ -50,7 +50,7 @@ #include #include -#define MODAUTHKERB_VERSION "5.3" +#define MODAUTHKERB_VERSION "5.4" #define MECH_NEGOTIATE "Negotiate" #define SERVICE_NAME "HTTP" @@ -80,6 +80,11 @@ #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 @@ -113,8 +118,7 @@ #include /* gethostbyname() */ #endif /* KRB4 */ -#ifndef _WIN32 -/* should be HAVE_UNISTD_H instead */ +#if HAVE_UNISTD_H #include #endif @@ -173,6 +177,13 @@ typedef struct { #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); @@ -292,7 +303,7 @@ mod_auth_kerb_rc_store(krb5_context context, krb5_rcache rcache, } /* 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, @@ -323,7 +334,7 @@ static void *kerb_dir_create_config(MK_POOL *p, char *d) ((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 @@ -441,7 +452,7 @@ authenticate_user_krb4pwd(request_rec *r, const char *auth_line) { int ret; - const char *sent_pw; + char *sent_pw; const char *sent_name; char *sent_instance; char tkt_file[32]; @@ -521,7 +532,7 @@ authenticate_user_krb4pwd(request_rec *r, 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) @@ -671,12 +682,16 @@ end: 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 @@ -691,9 +706,10 @@ verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal, 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", @@ -712,12 +728,42 @@ verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal, } */ - 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 @@ -886,7 +932,6 @@ store_krb5_creds(krb5_context kcontext, return OK; } - static int authenticate_user_krb5pwd(request_rec *r, kerb_auth_config *conf, @@ -907,10 +952,6 @@ authenticate_user_krb5pwd(request_rec *r, 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, @@ -991,7 +1032,7 @@ authenticate_user_krb5pwd(request_rec *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 */ @@ -1025,7 +1066,7 @@ authenticate_user_krb5pwd(request_rec *r, goto end; } MK_USER = apr_pstrdup (r->pool, name); - MK_AUTH_TYPE = "Basic"; + MK_AUTH_TYPE = "Kerberos"; free(name); if (conf->krb_save_credentials) @@ -1181,7 +1222,7 @@ get_gss_creds(request_rec *r, 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)) { @@ -1244,6 +1285,7 @@ get_gss_creds(request_rec *r, return 0; } +#ifndef GSSAPI_SUPPORTS_SPNEGO static int cmp_gss_type(gss_buffer_t token, gss_OID oid) { @@ -1270,6 +1312,7 @@ 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, @@ -1346,10 +1389,10 @@ 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, @@ -1364,7 +1407,7 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, &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; @@ -1402,15 +1445,15 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, 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); @@ -1512,15 +1555,44 @@ do_krb5_an_to_ln(request_rec *r) { #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 @@ -1560,19 +1632,99 @@ set_kerb_auth_headers(request_rec *r, const kerb_auth_config *conf, } 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); @@ -1600,60 +1752,15 @@ kerb_authenticate_user(request_rec *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; @@ -1736,6 +1843,14 @@ kerb_init_handler(apr_pool_t *p, apr_pool_t *plog, 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); }