X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=gss.c;h=cab10c3540b7bf45e858a116e4fb90b8fa088a8f;hb=2584e5b861e1af60fe7c4a331cfd7622cc8e955c;hp=6b04e53218cc10379570baf726d71e94271c93c4;hpb=5e863ddff6ef99a59acfb3c41f0b86ebc1468be7;p=mod_auth_kerb.cvs%2F.git diff --git a/gss.c b/gss.c index 6b04e53..cab10c3 100644 --- a/gss.c +++ b/gss.c @@ -1,6 +1,137 @@ +/* + * 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" -static const char * +void +gss_log(const char *file, + int line, +#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4 + int module_index, +#endif + 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, +#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4 + module_index, +#endif + level | APLOG_NOERRNO, + status, + r, + "%s", + errstr); +} + +apr_status_t +gss_cleanup_conn_ctx(void *data) +{ + gss_conn_ctx ctx = (gss_conn_ctx) data; + OM_uint32 minor_status; + + if (ctx && ctx->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER); + + if (ctx && ctx->server_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&minor_status, &ctx->server_creds); + + return APR_SUCCESS; +} + +gss_conn_ctx +gss_create_conn_ctx(request_rec *r, gss_auth_config *conf) +{ + char key[1024]; + gss_conn_ctx ctx = NULL; + + snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx"); + + if (NULL == (ctx = (gss_conn_ctx) apr_palloc(r->connection->pool, sizeof(*ctx)))) { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Can't allocate GSS context"); + return NULL; + } + ctx->context = GSS_C_NO_CONTEXT; + ctx->state = GSS_CTX_EMPTY; + ctx->filter_stat = GSS_FILT_NEW; + ctx->user = NULL; + + /* Acquire and store server credentials */ + if (0 == get_gss_creds(r, conf, &(ctx->server_creds))) { + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gss_create_conn_ctx: Server credentials acquired"); + } else { + gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Error: Server credentials NOT acquired"); + return NULL; + } + + apr_pool_userdata_set(ctx, key, gss_cleanup_conn_ctx, r->connection->pool); + + return ctx; +} + +gss_conn_ctx +gss_retrieve_conn_ctx(request_rec *r) +{ + char key[1024]; + gss_conn_ctx ctx = NULL; + + snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx"); + apr_pool_userdata_get((void **)&ctx, key, r->connection->pool); + + if (NULL == ctx) + gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gss_retrieve_conn_ctx: No GSS context found"); + + return ctx; +} + +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; +} + + +const char * get_gss_error(request_rec *r, OM_uint32 err_maj, OM_uint32 err_min, char *prefix) { OM_uint32 maj_stat, min_stat; @@ -53,7 +184,7 @@ get_gss_error(request_rec *r, OM_uint32 err_maj, OM_uint32 err_min, char *prefix return err_msg; } -static int +int get_gss_creds(request_rec *r, gss_auth_config *conf, gss_cred_id_t *server_creds) @@ -118,7 +249,7 @@ get_gss_creds(request_rec *r, return 0; } -static int +int cmp_gss_type(gss_buffer_t token, gss_OID oid) { unsigned char *p; @@ -145,178 +276,3 @@ cmp_gss_type(gss_buffer_t token, gss_OID oid) return memcmp(p, oid->elements, oid->length); } -int -gss_authenticate(request_rec *r, gss_auth_config *conf, gss_conn_ctx ctx, - const char *auth_line, char **negotiate_ret_value) -{ - 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 *); - - *negotiate_ret_value = "\0"; - - spnego_oid.length = 6; - spnego_oid.elements = (void *)"\x2b\x06\x01\x05\x05\x02"; - - 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 - } - - ret = get_gss_creds(r, conf, &server_creds); - if (ret) - goto end; - - /* 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")); - /* 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; - 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; -}