278N/A/*
278N/A * Wyllys Ingersoll <wyllys.ingersoll@sun.com>
278N/A *
278N/A * Based on work by
278N/A * Daniel Kouril <kouril@users.sourceforge.net>
278N/A * James E. Robinson, III <james@ncstate.net>
278N/A * Daniel Henninger <daniel@ncsu.edu>
278N/A * Ludek Sulak <xsulak@fi.muni.cz>
278N/A */
278N/A
278N/A/* ====================================================================
278N/A * The Apache Software License, Version 1.1
278N/A *
278N/A * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
278N/A * reserved.
278N/A *
278N/A * Redistribution and use in source and binary forms, with or without
278N/A * modification, are permitted provided that the following conditions
278N/A * are met:
278N/A *
278N/A * 1. Redistributions of source code must retain the above copyright
278N/A * notice, this list of conditions and the following disclaimer.
278N/A *
278N/A * 2. Redistributions in binary form must reproduce the above copyright
278N/A * notice, this list of conditions and the following disclaimer in
278N/A * the documentation and/or other materials provided with the
278N/A * distribution.
278N/A *
278N/A * 3. The end-user documentation included with the redistribution,
278N/A * if any, must include the following acknowledgment:
278N/A * "This product includes software developed by the
278N/A * Apache Software Foundation (http://www.apache.org/)."
278N/A * Alternately, this acknowledgment may appear in the software itself,
278N/A * if and wherever such third-party acknowledgments normally appear.
278N/A *
278N/A * 4. The names "Apache" and "Apache Software Foundation" must
278N/A * not be used to endorse or promote products derived from this
278N/A * software without prior written permission. For written
278N/A * permission, please contact apache@apache.org.
278N/A *
278N/A * 5. Products derived from this software may not be called "Apache",
278N/A * nor may "Apache" appear in their name, without prior written
278N/A * permission of the Apache Software Foundation.
278N/A *
278N/A * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
278N/A * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
278N/A * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
278N/A * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
278N/A * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
278N/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
278N/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
278N/A * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
278N/A * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
278N/A * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
278N/A * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278N/A * SUCH DAMAGE.
278N/A * ====================================================================
278N/A *
278N/A * This software consists of voluntary contributions made by many
278N/A * individuals on behalf of the Apache Software Foundation. For more
278N/A * information on the Apache Software Foundation, please see
278N/A * <http://www.apache.org/>.
278N/A *
278N/A * Portions of this software are based upon public domain software
278N/A * originally written at the National Center for Supercomputing Applications,
278N/A * University of Illinois, Urbana-Champaign.
278N/A */
278N/A
278N/A/*
278N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
278N/A */
278N/A
278N/A
278N/A#include <sys/types.h>
278N/A#include <strings.h>
278N/A
278N/A#include "httpd.h"
278N/A#include "http_config.h"
278N/A#include "http_core.h"
278N/A#include "http_log.h"
278N/A#include "http_protocol.h"
278N/A#include "http_request.h"
278N/A#include "ap_config.h"
278N/A#include "apr_base64.h"
278N/A#include "apr_lib.h"
278N/A#include "apr_time.h"
278N/A#include "apr_errno.h"
278N/A#include "apr_global_mutex.h"
278N/A#include "apr_strings.h"
278N/A#include "ap_compat.h"
278N/A
278N/A#include <gssapi/gssapi.h>
278N/A#include <gssapi/gssapi_ext.h>
278N/A
278N/Amodule auth_gss_module;
278N/A
278N/Astatic void *gss_create_dir_config(apr_pool_t *, char *);
278N/A
278N/Aint gss_authenticate(request_rec *);
278N/A
278N/Atypedef struct {
278N/A char *gss_service_name;
278N/A char *keytab_file;
278N/A int gss_debug;
278N/A} gss_auth_config;
278N/A
278N/Astatic const char *set_service_name(cmd_parms *cmd, void *config,
278N/A const char *name)
278N/A{
278N/A ((gss_auth_config *) config)->gss_service_name = (char *)name;
278N/A return NULL;
278N/A}
278N/A
278N/Astatic const char *set_keytab_file(cmd_parms *cmd, void *config,
278N/A const char *file)
278N/A{
278N/A ((gss_auth_config *) config)->keytab_file = (char *)file;
278N/A return NULL;
278N/A}
278N/A
278N/Astatic const char *set_gss_debug(cmd_parms *cmd, void *config,
278N/A const char *debugflag)
278N/A{
278N/A ((gss_auth_config *) config)->gss_debug = atoi(debugflag);
278N/A return NULL;
278N/A}
278N/A
278N/Astatic const command_rec gss_auth_cmds[] = {
278N/A AP_INIT_TAKE1("AuthGSSServiceName", set_service_name, NULL,
278N/A OR_AUTHCFG, "Service name used for authentication."),
278N/A
278N/A AP_INIT_TAKE1("AuthGSSKeytabFile", set_keytab_file, NULL,
278N/A OR_AUTHCFG,
278N/A "Location of Kerberos V5 keytab file."),
278N/A
278N/A AP_INIT_TAKE1("AuthGssDebug", set_gss_debug, NULL,
278N/A OR_AUTHCFG,
278N/A "Enable debug logging in error_log"),
278N/A { NULL }
278N/A};
278N/A
278N/Astatic void
278N/Agss_register_hooks(apr_pool_t *p)
278N/A{
278N/A ap_hook_check_user_id(gss_authenticate,NULL,NULL,APR_HOOK_MIDDLE);
278N/A}
278N/A
278N/Amodule AP_MODULE_DECLARE_DATA auth_gss_module = {
278N/A STANDARD20_MODULE_STUFF,
278N/A gss_create_dir_config, /* dir config creater */
278N/A NULL, /* dir merger --- default is to override */
278N/A NULL, /* server config */
278N/A NULL, /* merge server config */
278N/A gss_auth_cmds, /* command apr_table_t */
278N/A gss_register_hooks /* register hooks */
278N/A};
278N/A
278N/Atypedef struct {
278N/A gss_ctx_id_t context;
278N/A gss_cred_id_t server_creds;
278N/A} gss_connection_t;
278N/A
278N/Astatic gss_connection_t *gss_connection = NULL;
278N/A
278N/Astatic void *
278N/Agss_create_dir_config(apr_pool_t *p, char *d)
278N/A{
278N/A gss_auth_config *rec =
278N/A (gss_auth_config *) apr_pcalloc(p, sizeof(gss_auth_config));
278N/A
278N/A ((gss_auth_config *)rec)->gss_service_name = "HTTP";
278N/A ((gss_auth_config *)rec)->keytab_file = "/var/apache2/http.keytab";
278N/A ((gss_auth_config *)rec)->gss_debug = 0;
278N/A
278N/A return rec;
278N/A}
278N/A
278N/Avoid log_rerror(const char *file, int line, int level, int status,
278N/A const request_rec *r, const char *fmt, ...)
278N/A{
278N/A char errstr[1024];
278N/A va_list ap;
278N/A
278N/A va_start(ap, fmt);
278N/A vsnprintf(errstr, sizeof(errstr), fmt, ap);
278N/A va_end(ap);
278N/A
278N/A ap_log_rerror(file, line, level | APLOG_NOERRNO, NULL, r, "%s", errstr);
278N/A}
278N/A
278N/A/*********************************************************************
278N/A * GSSAPI Authentication
278N/A ********************************************************************/
278N/Astatic const char *
278N/Agss_error_msg(apr_pool_t *p, OM_uint32 maj, OM_uint32 min, char *prefix)
278N/A{
278N/A OM_uint32 maj_stat, min_stat;
278N/A OM_uint32 msg_ctx = 0;
278N/A gss_buffer_desc msg;
278N/A
278N/A char *err_msg = (char *)apr_pstrdup(p, prefix);
278N/A
278N/A do {
278N/A maj_stat = gss_display_status (&min_stat,
278N/A maj, GSS_C_GSS_CODE,
278N/A GSS_C_NO_OID, &msg_ctx,
278N/A &msg);
278N/A if (GSS_ERROR(maj_stat))
278N/A break;
278N/A
278N/A err_msg = apr_pstrcat(p, err_msg, ": ", (char*) msg.value,
278N/A NULL);
278N/A (void) gss_release_buffer(&min_stat, &msg);
278N/A
278N/A maj_stat = gss_display_status (&min_stat,
278N/A min, GSS_C_MECH_CODE,
278N/A GSS_C_NULL_OID, &msg_ctx,
278N/A &msg);
278N/A if (!GSS_ERROR(maj_stat)) {
278N/A err_msg = apr_pstrcat(p, err_msg,
278N/A " (", (char*) msg.value, ")", NULL);
278N/A (void) gss_release_buffer(&min_stat, &msg);
278N/A }
278N/A } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
278N/A
278N/A return (err_msg);
278N/A}
278N/A
278N/Astatic int
278N/Acleanup_gss_connection(void *data)
278N/A{
278N/A OM_uint32 ret;
278N/A OM_uint32 minor_status;
278N/A gss_connection_t *gss_conn = (gss_connection_t *)data;
278N/A
278N/A if (data == NULL)
278N/A return 0;
278N/A
278N/A if (gss_conn->context != GSS_C_NO_CONTEXT) {
278N/A (void) gss_delete_sec_context(&minor_status,
278N/A &gss_conn->context,
278N/A GSS_C_NO_BUFFER);
278N/A }
278N/A
278N/A if (gss_conn->server_creds != GSS_C_NO_CREDENTIAL) {
278N/A (void) gss_release_cred(&minor_status, &gss_conn->server_creds);
278N/A }
278N/A
278N/A gss_connection = NULL;
278N/A
278N/A return 0;
278N/A}
278N/A
278N/Astatic int
278N/Aacquire_server_creds(request_rec *r,
278N/A gss_auth_config *conf,
278N/A gss_OID_set mechset,
278N/A gss_cred_id_t *server_creds)
278N/A{
278N/A int ret = 0;
278N/A gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
278N/A OM_uint32 major_status, minor_status, minor_status2;
278N/A gss_name_t server_name = GSS_C_NO_NAME;
278N/A char buf[1024];
278N/A
278N/A snprintf(buf, sizeof(buf), "%s@%s",
278N/A conf->gss_service_name, r->hostname);
278N/A
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "acquire_server_creds for %s", buf);
278N/A
278N/A input_token.value = buf;
278N/A input_token.length = strlen(buf) + 1;
278N/A
278N/A major_status = gss_import_name(&minor_status, &input_token,
278N/A GSS_C_NT_HOSTBASED_SERVICE,
278N/A &server_name);
278N/A
278N/A if (GSS_ERROR(major_status)) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "%s", gss_error_msg(r->pool, major_status, minor_status,
278N/A "gss_import_name() failed"));
278N/A return (HTTP_INTERNAL_SERVER_ERROR);
278N/A }
278N/A
278N/A major_status = gss_acquire_cred(&minor_status, server_name,
278N/A GSS_C_INDEFINITE,
278N/A mechset, GSS_C_ACCEPT,
278N/A server_creds, NULL, NULL);
278N/A
278N/A if (GSS_ERROR(major_status)) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "%s", gss_error_msg(r->pool, major_status, minor_status,
278N/A "gss_acquire_cred() failed"));
278N/A ret = HTTP_INTERNAL_SERVER_ERROR;
278N/A }
278N/A (void) gss_release_name(&minor_status2, &server_name);
278N/A
278N/A return (ret);
278N/A}
278N/A
278N/Astatic int
278N/Aauthenticate_user_gss(request_rec *r, gss_auth_config *conf,
278N/A const char *auth_line, char **negotiate_ret_value)
278N/A{
278N/A int ret = 0;
278N/A OM_uint32 major_status, minor_status, minor_status2;
278N/A gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
278N/A gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
278N/A const char *auth_param = NULL;
278N/A gss_name_t client_name = GSS_C_NO_NAME;
278N/A gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
278N/A
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "authenticate_user_gss called");
278N/A
278N/A *negotiate_ret_value = (char *)"";
278N/A
278N/A if (gss_connection == NULL) {
278N/A gss_connection = apr_pcalloc(r->connection->pool, sizeof(*gss_connection));
278N/A if (gss_connection == NULL) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "apr_pcalloc() failed (not enough memory)");
278N/A ret = HTTP_INTERNAL_SERVER_ERROR;
278N/A goto end;
278N/A }
278N/A (void) memset(gss_connection, 0, sizeof(*gss_connection));
278N/A apr_pool_cleanup_register(r->connection->pool, gss_connection,
278N/A cleanup_gss_connection, apr_pool_cleanup_null);
278N/A }
278N/A
278N/A if (conf->keytab_file) {
278N/A char *ktname;
278N/A /*
278N/A * We don't use the ap_* calls here, since the string
278N/A * passed to putenv() will become part of the enviroment
278N/A * and shouldn't be free()ed by apache.
278N/A */
278N/A ktname = malloc(strlen("KRB5_KTNAME=") + strlen(conf->keytab_file) + 1);
278N/A if (ktname == NULL) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "malloc() failed: not enough memory");
278N/A ret = HTTP_INTERNAL_SERVER_ERROR;
278N/A goto end;
278N/A }
278N/A /*
278N/A * Put the keytab name in the environment so that Kerberos
278N/A * knows where to look later.
278N/A */
278N/A sprintf(ktname, "KRB5_KTNAME=%s", conf->keytab_file);
278N/A putenv(ktname);
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Using keytab: %s", ktname);
278N/A }
278N/A
278N/A /* ap_getword() shifts parameter */
278N/A auth_param = ap_getword_white(r->pool, &auth_line);
278N/A if (auth_param == NULL) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "No Authorization parameter in request from client");
278N/A ret = HTTP_UNAUTHORIZED;
278N/A goto end;
278N/A }
278N/A
278N/A input_token.length = apr_base64_decode_len(auth_param) + 1;
278N/A input_token.value = apr_pcalloc(r->connection->pool, input_token.length);
278N/A
278N/A if (input_token.value == NULL) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "apr_pcalloc() failed (not enough memory)");
278N/A ret = HTTP_INTERNAL_SERVER_ERROR;
278N/A goto end;
278N/A }
278N/A input_token.length = apr_base64_decode(input_token.value, auth_param);
278N/A
278N/A if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
278N/A gss_OID_set_desc desiredMechs;
278N/A gss_OID_desc client_mech_desc;
278N/A gss_OID client_mechoid = &client_mech_desc;
278N/A char *mechstr = NULL;
278N/A
278N/A if (!__gss_get_mech_type(client_mechoid, &input_token)) {
278N/A mechstr = (char *)__gss_oid_to_mech(client_mechoid);
278N/A }
278N/A if (mechstr == NULL) {
278N/A client_mechoid = GSS_C_NULL_OID;
278N/A mechstr = "<unknown>";
278N/A }
278N/A
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "Client wants GSS mech: %s", mechstr);
278N/A
278N/A desiredMechs.count = 1;
278N/A desiredMechs.elements = client_mechoid;
278N/A
278N/A /* Get creds using the mechanism that the client requested */
278N/A ret = acquire_server_creds(r, conf, &desiredMechs,
278N/A &gss_connection->server_creds);
278N/A if (ret)
278N/A goto end;
278N/A }
278N/A /*
278N/A * Try to display the server creds information.
278N/A */
278N/A if (conf->gss_debug) {
278N/A gss_name_t sname;
278N/A gss_buffer_desc dname;
278N/A
278N/A major_status = gss_inquire_cred(&minor_status,
278N/A gss_connection->server_creds,
278N/A &sname, NULL, NULL, NULL);
278N/A if (major_status == GSS_S_COMPLETE) {
278N/A major_status = gss_display_name(&minor_status,
278N/A sname, &dname, NULL);
278N/A }
278N/A if (major_status == GSS_S_COMPLETE) {
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "got server creds for: %.*s",
278N/A (int)dname.length,
278N/A (char *)dname.value);
278N/A (void) gss_release_name(&minor_status, &sname);
278N/A (void) gss_release_buffer(&minor_status, &dname);
278N/A }
278N/A }
278N/A
278N/A major_status = gss_accept_sec_context(&minor_status,
278N/A &gss_connection->context,
278N/A gss_connection->server_creds,
278N/A &input_token,
278N/A GSS_C_NO_CHANNEL_BINDINGS,
278N/A &client_name,
278N/A NULL,
278N/A &output_token,
278N/A NULL,
278N/A NULL,
278N/A &delegated_cred);
278N/A
278N/A if (output_token.length) {
278N/A char *token = NULL;
278N/A size_t len;
278N/A len = apr_base64_encode_len(output_token.length) + 1;
278N/A token = apr_pcalloc(r->connection->pool, len + 1);
278N/A if (token == NULL) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "apr_pcalloc() failed (not enough memory)");
278N/A ret = HTTP_INTERNAL_SERVER_ERROR;
278N/A gss_release_buffer(&minor_status2, &output_token);
278N/A goto end;
278N/A }
278N/A apr_base64_encode(token, output_token.value, output_token.length);
278N/A token[len] = '\0';
278N/A *negotiate_ret_value = token;
278N/A }
278N/A
278N/A if (GSS_ERROR(major_status)) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "%s", gss_error_msg(r->pool, major_status, minor_status,
278N/A "gss_accept_sec_context() failed"));
278N/A /* Don't offer the Negotiate method again if call to GSS layer failed */
278N/A *negotiate_ret_value = NULL;
278N/A ret = HTTP_UNAUTHORIZED;
278N/A goto end;
278N/A }
278N/A
278N/A if (major_status == GSS_S_CONTINUE_NEEDED) {
278N/A /*
278N/A * Some GSSAPI mechanisms may require multiple iterations to
278N/A * establish authentication. Most notably, when MUTUAL_AUTHENTICATION
278N/A * flag is used, multiple round trips are needed.
278N/A */
278N/A ret = HTTP_UNAUTHORIZED;
278N/A goto end;
278N/A }
278N/A
278N/A if (client_name != GSS_C_NO_NAME) {
278N/A gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
278N/A major_status = gss_display_name(&minor_status, client_name,
278N/A &name_token, NULL);
278N/A
278N/A if (GSS_ERROR(major_status)) {
278N/A log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
278N/A "%s", gss_error_msg(r->pool, major_status,
278N/A minor_status,
278N/A "gss_export_name() failed"));
278N/A ret = HTTP_INTERNAL_SERVER_ERROR;
278N/A goto end;
278N/A }
278N/A if (name_token.length) {
278N/A r->user = apr_pstrdup(r->pool, name_token.value);
278N/A gss_release_buffer(&minor_status, &name_token);
278N/A }
278N/A
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "Authenticated user: %s",
278N/A r->user ? r->user : "<unknown>");
278N/A }
278N/A r->ap_auth_type = "Negotiate";
278N/A ret = OK;
278N/Aend:
278N/A if (delegated_cred)
278N/A gss_release_cred(&minor_status, &delegated_cred);
278N/A
278N/A if (output_token.length)
278N/A gss_release_buffer(&minor_status, &output_token);
278N/A
278N/A if (client_name != GSS_C_NO_NAME)
278N/A gss_release_name(&minor_status, &client_name);
278N/A
278N/A cleanup_gss_connection(gss_connection);
278N/A
278N/A return ret;
278N/A}
278N/A
278N/Astatic int
278N/Aalready_succeeded(request_rec *r)
278N/A{
278N/A if (ap_is_initial_req(r) || r->ap_auth_type == NULL)
278N/A return 0;
278N/A
278N/A return (strcmp(r->ap_auth_type, "Negotiate") ||
278N/A (strcmp(r->ap_auth_type, "Basic") && strchr(r->user, '@')));
278N/A}
278N/A
278N/Astatic void
278N/Anote_gss_auth_failure(request_rec *r, const gss_auth_config *conf,
278N/A char *negotiate_ret_value)
278N/A{
278N/A const char *auth_name = NULL;
278N/A int set_basic = 0;
278N/A char *negoauth_param;
278N/A
278N/A /* get the user realm specified in .htaccess */
278N/A auth_name = ap_auth_name(r);
278N/A
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "note_gss_auth_failure: auth_name = %s",
278N/A auth_name ? auth_name : "<undefined>");
278N/A
278N/A if (negotiate_ret_value != NULL) {
278N/A negoauth_param = (*negotiate_ret_value == '\0') ? "Negotiate" :
278N/A apr_pstrcat(r->pool, "Negotiate ", negotiate_ret_value, NULL);
278N/A apr_table_add(r->err_headers_out, "WWW-Authenticate", negoauth_param);
278N/A }
278N/A}
278N/A
278N/Aint
278N/Agss_authenticate(request_rec *r)
278N/A{
278N/A int ret;
278N/A gss_auth_config *conf =
278N/A (gss_auth_config *) ap_get_module_config(r->per_dir_config,
278N/A &auth_gss_module);
278N/A const char *auth_type = NULL;
278N/A const char *auth_line = NULL;
278N/A const char *type = NULL;
278N/A char *negotiate_ret_value;
278N/A static int last_return = HTTP_UNAUTHORIZED;
278N/A
278N/A /* get the type specified in .htaccess */
278N/A type = ap_auth_type(r);
278N/A
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "gss_authenticate: type = %s", type);
278N/A
278N/A if (type == NULL || (strcasecmp(type, "GSSAPI") != 0)) {
278N/A return DECLINED;
278N/A }
278N/A
278N/A /* get what the user sent us in the HTTP header */
278N/A auth_line = apr_table_get(r->headers_in, "Authorization");
278N/A
278N/A if (!auth_line) {
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "No authentication data found");
278N/A note_gss_auth_failure(r, conf, "\0");
278N/A return HTTP_UNAUTHORIZED;
278N/A }
278N/A auth_type = ap_getword_white(r->pool, &auth_line);
278N/A
278N/A if (already_succeeded(r))
278N/A return last_return;
278N/A
278N/A if (strcasecmp(auth_type, "Negotiate") == 0) {
278N/A ret = authenticate_user_gss(r, conf, auth_line, &negotiate_ret_value);
278N/A } else {
278N/A ret = HTTP_UNAUTHORIZED;
278N/A }
278N/A
278N/A if (ret == HTTP_UNAUTHORIZED) {
278N/A if (conf->gss_debug)
278N/A log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
278N/A "Authentication failed.");
278N/A note_gss_auth_failure(r, conf, negotiate_ret_value);
278N/A }
278N/A
278N/A last_return = ret;
278N/A return ret;
278N/A}