X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=mod_auth_gssapi.c;h=3f255b91adb277bbfad625aa8eee63bf978fa642;hb=refs%2Fheads%2Fgssweb-apache;hp=fb14416c08d1c14376d48f2df876f5042cbd1c11;hpb=5e863ddff6ef99a59acfb3c41f0b86ebc1468be7;p=mod_auth_kerb.cvs%2F.git diff --git a/mod_auth_gssapi.c b/mod_auth_gssapi.c index fb14416..3f255b9 100644 --- a/mod_auth_gssapi.c +++ b/mod_auth_gssapi.c @@ -1,3 +1,34 @@ +/* + * Copyright (c) 2010 CESNET + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + #include "mod_auth_gssapi.h" module AP_MODULE_DECLARE_DATA auth_gssapi_module; @@ -17,29 +48,6 @@ static const command_rec gss_config_cmds[] = { { NULL } }; -static void * -gss_config_dir_create(apr_pool_t *p, char *d) -{ - gss_auth_config *conf; - - conf = (gss_auth_config *) apr_pcalloc(p, sizeof(*conf)); - return conf; -} - -void -gss_log(const char *file, int line, int level, int status, - const request_rec *r, const char *fmt, ...) -{ - char errstr[1024]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(errstr, sizeof(errstr), fmt, ap); - va_end(ap); - - ap_log_rerror(file, line, level | APLOG_NOERRNO, status, r, "%s", errstr); -} - static void set_http_headers(request_rec *r, const gss_auth_config *conf, char *negotiate_ret_value) @@ -51,42 +59,186 @@ set_http_headers(request_rec *r, const gss_auth_config *conf, if (negotiate_ret_value == NULL) return; - negoauth_param = (*negotiate_ret_value == '\0') ? "GSSAPI" : - apr_pstrcat(r->pool, "GSSAPI ", negotiate_ret_value, NULL); + negoauth_param = (*negotiate_ret_value == '\0') ? "Negotiate" : + apr_pstrcat(r->pool, "Negotiate ", negotiate_ret_value, NULL); apr_table_add(r->err_headers_out, header_name, negoauth_param); } -static apr_status_t -cleanup_conn_ctx(void *data) +static int +gss_authenticate(request_rec *r, gss_auth_config *conf, gss_conn_ctx ctx, + const char *auth_line, char **negotiate_ret_value) { - gss_conn_ctx ctx = (gss_conn_ctx) data; - OM_uint32 minor_status; + OM_uint32 major_status, minor_status, minor_status2; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + const char *auth_param = NULL; + int ret; + gss_name_t client_name = GSS_C_NO_NAME; + gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; + gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; + OM_uint32 ret_flags = 0; + gss_OID_desc spnego_oid; + OM_uint32 (*accept_sec_context) + (OM_uint32 *, gss_ctx_id_t *, const gss_cred_id_t, + const gss_buffer_t, const gss_channel_bindings_t, + gss_name_t *, gss_OID *, gss_buffer_t, OM_uint32 *, + OM_uint32 *, gss_cred_id_t *); - if (ctx && ctx->context != GSS_C_NO_CONTEXT) - gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER); + *negotiate_ret_value = "\0"; - return APR_SUCCESS; -} + spnego_oid.length = 6; + spnego_oid.elements = (void *)"\x2b\x06\x01\x05\x05\x02"; -static gss_conn_ctx -gss_get_conn_ctx(request_rec *r) -{ - char key[1024]; - gss_conn_ctx ctx = NULL; - - snprintf(key, sizeof(key), "mod_auth_gssapi:conn_ctx"); - apr_pool_userdata_get((void **)&ctx, key, r->connection->pool); - /* XXX LOG */ - if (ctx == NULL) { - ctx = (gss_conn_ctx) apr_palloc(r->connection->pool, sizeof(*ctx)); - if (ctx == NULL) - return NULL; - ctx->context = GSS_C_NO_CONTEXT; - ctx->state = GSS_CTX_EMPTY; - ctx->user = NULL; - apr_pool_userdata_set(ctx, key, cleanup_conn_ctx, r->connection->pool); - } - return ctx; + if (conf->krb5_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->krb5_keytab) + 1); + if (ktname == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, "malloc() failed: not enough memory"); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + sprintf(ktname, "KRB5_KTNAME=%s", conf->krb5_keytab); + putenv(ktname); +#ifdef HEIMDAL + /* Seems to be also supported by latest MIT */ + gsskrb5_register_acceptor_identity(conf->krb_5_keytab); +#endif + } + + /* ap_getword() shifts parameter */ + auth_param = ap_getword_white(r->pool, &auth_line); + if (auth_param == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "No Authorization parameter in request from client"); + ret = HTTP_UNAUTHORIZED; + goto end; + } + + if (ctx->state == GSS_CTX_ESTABLISHED) { + gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER); + ctx->context = GSS_C_NO_CONTEXT; + ctx->state = GSS_CTX_EMPTY; + } + + input_token.length = apr_base64_decode_len(auth_param) + 1; + input_token.value = apr_pcalloc(r->connection->pool, input_token.length); + if (input_token.value == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "ap_pcalloc() failed (not enough memory)"); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + input_token.length = apr_base64_decode(input_token.value, auth_param); + + /* LOG length, type */ + +#ifdef GSSAPI_SUPPORTS_SPNEGO + accept_sec_context = gss_accept_sec_context; +#else + accept_sec_context = (cmp_gss_type(&input_token, &spnego_oid) == 0) ? + gss_accept_sec_context_spnego : gss_accept_sec_context; +#endif + + major_status = accept_sec_context(&minor_status, + &ctx->context, + server_creds, + &input_token, + GSS_C_NO_CHANNEL_BINDINGS, + NULL, + NULL, + &output_token, + &ret_flags, + NULL, + &delegated_cred); + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "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; + + len = apr_base64_encode_len(output_token.length) + 1; + token = apr_pcalloc(r->connection->pool, len + 1); + if (token == NULL) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "ap_pcalloc() failed (not enough memory)"); + ret = HTTP_INTERNAL_SERVER_ERROR; + gss_release_buffer(&minor_status2, &output_token); + goto end; + } + apr_base64_encode(token, output_token.value, output_token.length); + token[len] = '\0'; + *negotiate_ret_value = token; + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, + "GSS-API token of length %d bytes will be sent back", + output_token.length); + gss_release_buffer(&minor_status2, &output_token); + } + + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "Failed to establish authentication")); +#if 0 + /* Don't offer the Negotiate method again if call to GSS layer failed */ + /* XXX ... which means we don't return the "error" output */ + *negotiate_ret_value = NULL; +#endif + gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER); + ctx->context = GSS_C_NO_CONTEXT; + ctx->state = GSS_CTX_EMPTY; + ret = HTTP_UNAUTHORIZED; + goto end; + } + + if (major_status & GSS_S_CONTINUE_NEEDED) { + ctx->state = GSS_CTX_IN_PROGRESS; + ret = HTTP_UNAUTHORIZED; + goto end; + } + + major_status = gss_inquire_context(&minor_status, ctx->context, &client_name, + NULL, NULL, NULL, NULL, NULL, NULL); + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, "gss_inquire_context() failed")); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); + gss_release_name(&minor_status, &client_name); + if (GSS_ERROR(major_status)) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, + "%s", get_gss_error(r, major_status, minor_status, + "gss_display_name() failed")); + ret = HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + ctx->state = GSS_CTX_ESTABLISHED; + ctx->user = apr_pstrdup(r->pool, output_token.value); + gss_release_buffer(&minor_status, &output_token); + + ret = OK; + +end: + if (delegated_cred) + gss_release_cred(&minor_status, &delegated_cred); + + if (output_token.length) + gss_release_buffer(&minor_status, &output_token); + + if (client_name != GSS_C_NO_NAME) + gss_release_name(&minor_status, &client_name); + + if (server_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&minor_status, &server_creds); + + return ret; } static int @@ -106,7 +258,7 @@ gss_authenticate_user(request_rec *r) /* get the type specified in Apache configuration */ type = ap_auth_type(r); - if (type == NULL || strcmp(type, "GSSAPI") != 0) { + if (type == NULL || strcmp(type, "Negotiate") != 0) { gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "AuthType '%s' is not for us, bailing out", (type) ? type : "(NULL)"); @@ -126,7 +278,7 @@ gss_authenticate_user(request_rec *r) } auth_type = ap_getword_white(r->pool, &auth_line); - if (strcasecmp(auth_type, "GSSAPI") != 0) { + if (strcasecmp(auth_type, "Negotiate") != 0) { gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Unsupported authentication type (%s) requested by client", (auth_type) ? auth_type : "(NULL)"); @@ -134,8 +286,8 @@ gss_authenticate_user(request_rec *r) return HTTP_UNAUTHORIZED; } - conn_ctx = gss_get_conn_ctx(r); - if (conn_ctx == NULL) { + if ((NULL == (conn_ctx = gss_retrieve_conn_ctx(r))) && + (NULL == (conn_ctx = gss_create_conn_ctx(r, conf)))) { gss_log(APLOG_MARK, APLOG_ERR, 0, r, "Failed to create internal context: probably not enough memory"); return HTTP_INTERNAL_SERVER_ERROR; @@ -144,14 +296,14 @@ gss_authenticate_user(request_rec *r) /* optimizing hack */ if (conn_ctx->state == GSS_CTX_ESTABLISHED && auth_line == NULL) { r->user = apr_pstrdup(r->pool, conn_ctx->user); - r->ap_auth_type = "GSSAPI"; + r->ap_auth_type = "Negotiate"; return OK; } /* XXXX subrequests ignored, only successful accesses taken into account! */ if (!ap_is_initial_req(r) && conn_ctx->state == GSS_CTX_ESTABLISHED) { r->user = apr_pstrdup(r->pool, conn_ctx->user); - r->ap_auth_type = "GSSAPI"; + r->ap_auth_type = "Negotiate"; return OK; } @@ -164,7 +316,7 @@ gss_authenticate_user(request_rec *r) if (ret == OK) { r->user = apr_pstrdup(r->pool, conn_ctx->user); - r->ap_auth_type = "GSSAPI"; + r->ap_auth_type = "Negotiate"; } /* debug LOG ??? */