X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmod_auth_kerb.c;h=e5e9116b9482bfaddddff31914cf7cdf2d838e91;hb=884d31e42e398c10e8ae48a13918a8acda769900;hp=71ce09914157be35bdf6b3452a287b0319fa4113;hpb=c132885606351cb9e9385b6b5b900c139b1ffb35;p=mod_auth_kerb.cvs%2F.git diff --git a/src/mod_auth_kerb.c b/src/mod_auth_kerb.c index 71ce099..e5e9116 100644 --- a/src/mod_auth_kerb.c +++ b/src/mod_auth_kerb.c @@ -50,7 +50,7 @@ #include #include -#define MODAUTHKERB_VERSION "5.1" +#define MODAUTHKERB_VERSION "5.3" #define MECH_NEGOTIATE "Negotiate" #define SERVICE_NAME "HTTP" @@ -113,11 +113,18 @@ #include /* gethostbyname() */ #endif /* KRB4 */ -#ifndef _WIN32 -/* should be HAVE_UNISTD_H instead */ +#if HAVE_UNISTD_H #include #endif +#ifndef KRB5_LIB_FUNCTION +# if defined(_WIN32) +# define KRB5_LIB_FUNCTION _stdcall +# else +# define KRB5_LIB_FUNCTION +# endif +#endif + #ifdef STANDARD20_MODULE_STUFF module AP_MODULE_DECLARE_DATA auth_kerb_module; #else @@ -157,6 +164,7 @@ typedef struct { 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; @@ -164,6 +172,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); @@ -219,6 +234,9 @@ static const command_rec kerb_auth_cmds[] = { 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 @@ -295,7 +313,6 @@ const krb5_rc_ops_internal mod_auth_kerb_rc_ops = { }; #endif - /*************************************************************************** Auth Configuration Initialization ***************************************************************************/ @@ -312,6 +329,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)->krb_method_k5pass = 1; ((kerb_auth_config *)rec)->krb_method_gssapi = 1; #endif @@ -429,7 +447,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]; @@ -442,7 +460,7 @@ authenticate_user_krb4pwd(request_rec *r, 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) @@ -556,7 +574,12 @@ verify_krb5_init_creds(request_rec *r, krb5_context context, krb5_creds *creds, } 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"); @@ -654,12 +677,15 @@ 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, 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 @@ -674,9 +700,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", @@ -695,15 +722,50 @@ 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 + 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", @@ -893,7 +955,7 @@ authenticate_user_krb5pwd(request_rec *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, @@ -965,7 +1027,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 */ @@ -1029,14 +1091,18 @@ end: ********************************************************************/ 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, @@ -1044,11 +1110,16 @@ get_gss_error(MK_POOL *p, OM_uint32 err_maj, OM_uint32 err_min, char *prefix) 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, @@ -1056,11 +1127,12 @@ get_gss_error(MK_POOL *p, OM_uint32 err_maj, OM_uint32 err_min, char *prefix) &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; } @@ -1100,7 +1172,7 @@ store_gss_creds(request_rec *r, kerb_auth_config *conf, char *princ_name, 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; } @@ -1132,6 +1204,10 @@ get_gss_creds(request_rec *r, 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, @@ -1141,12 +1217,12 @@ 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)) { 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; } @@ -1156,7 +1232,7 @@ get_gss_creds(request_rec *r, /* 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; } @@ -1171,7 +1247,7 @@ get_gss_creds(request_rec *r, 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; } @@ -1250,6 +1326,7 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, 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"; @@ -1305,10 +1382,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, @@ -1318,11 +1395,12 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, &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) ? "delegated" : "didn't delegate"); if (output_token.length) { char *token = NULL; size_t len; @@ -1352,7 +1430,7 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, "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; @@ -1374,7 +1452,7 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, 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; @@ -1385,7 +1463,7 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf, 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; @@ -1408,17 +1486,88 @@ end: 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; + 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 @@ -1463,13 +1612,15 @@ kerb_authenticate_user(request_rec *r) 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; + char *auth_line = NULL; const char *type = NULL; int use_krb5 = 0, use_krb4 = 0; 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); @@ -1498,7 +1649,7 @@ 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) { @@ -1506,7 +1657,7 @@ kerb_authenticate_user(request_rec *r) (use_krb5) ? "\0" : NULL); return HTTP_UNAUTHORIZED; } - auth_type = ap_getword_white(r->pool, &auth_line); + 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 && @@ -1519,10 +1670,8 @@ kerb_authenticate_user(request_rec *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 && @@ -1543,6 +1692,30 @@ kerb_authenticate_user(request_rec *r) 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; @@ -1554,18 +1727,18 @@ have_rcache_type(const char *type) { krb5_error_code ret; krb5_context context; - krb5_rcache id; + krb5_rcache id = NULL; int found; - memset(&id, 0, sizeof(id)); - ret = krb5_init_context(&context); if (ret) return 0; - ret = krb5_rc_resolve_type(context, &id, type); + ret = krb5_rc_resolve_full(context, &id, "none:"); found = (ret == 0); + if (ret == 0) + krb5_rc_destroy(context, id); krb5_free_context(context); return found;