components/apache2/mod_auth_gss/mod_auth_gss.c
changeset 5172 2ac937ed383b
parent 5171 4e8b3c0ea78c
child 5173 fe10c87a291e
equal deleted inserted replaced
5171:4e8b3c0ea78c 5172:2ac937ed383b
     1 /*
       
     2  * Wyllys Ingersoll <[email protected]>
       
     3  *
       
     4  * Based on work by
       
     5  *   Daniel Kouril <[email protected]>
       
     6  *   James E. Robinson, III <[email protected]>
       
     7  *   Daniel Henninger <[email protected]>
       
     8  *   Ludek Sulak <[email protected]>
       
     9  */
       
    10 
       
    11 /* ====================================================================
       
    12  * The Apache Software License, Version 1.1
       
    13  *
       
    14  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
       
    15  * reserved.
       
    16  *
       
    17  * Redistribution and use in source and binary forms, with or without
       
    18  * modification, are permitted provided that the following conditions
       
    19  * are met:
       
    20  *
       
    21  * 1. Redistributions of source code must retain the above copyright
       
    22  *    notice, this list of conditions and the following disclaimer.
       
    23  *
       
    24  * 2. Redistributions in binary form must reproduce the above copyright
       
    25  *    notice, this list of conditions and the following disclaimer in
       
    26  *    the documentation and/or other materials provided with the
       
    27  *    distribution.
       
    28  *
       
    29  * 3. The end-user documentation included with the redistribution,
       
    30  *    if any, must include the following acknowledgment:
       
    31  *       "This product includes software developed by the
       
    32  *        Apache Software Foundation (http://www.apache.org/)."
       
    33  *    Alternately, this acknowledgment may appear in the software itself,
       
    34  *    if and wherever such third-party acknowledgments normally appear.
       
    35  *
       
    36  * 4. The names "Apache" and "Apache Software Foundation" must
       
    37  *    not be used to endorse or promote products derived from this
       
    38  *    software without prior written permission. For written
       
    39  *    permission, please contact [email protected].
       
    40  *
       
    41  * 5. Products derived from this software may not be called "Apache",
       
    42  *    nor may "Apache" appear in their name, without prior written
       
    43  *    permission of the Apache Software Foundation.
       
    44  *
       
    45  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
       
    46  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
       
    47  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    48  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
       
    49  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    50  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    51  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
       
    52  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    53  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
       
    54  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
       
    55  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
       
    56  * SUCH DAMAGE.
       
    57  * ====================================================================
       
    58  *
       
    59  * This software consists of voluntary contributions made by many
       
    60  * individuals on behalf of the Apache Software Foundation.  For more
       
    61  * information on the Apache Software Foundation, please see
       
    62  * <http://www.apache.org/>.
       
    63  *
       
    64  * Portions of this software are based upon public domain software
       
    65  * originally written at the National Center for Supercomputing Applications,
       
    66  * University of Illinois, Urbana-Champaign.
       
    67  */
       
    68 
       
    69 /*
       
    70  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
       
    71  */
       
    72 
       
    73 
       
    74 #include <sys/types.h>
       
    75 #include <strings.h>
       
    76 
       
    77 #include "httpd.h"
       
    78 #include "http_config.h"
       
    79 #include "http_core.h"
       
    80 #include "http_log.h"
       
    81 #include "http_protocol.h"
       
    82 #include "http_request.h"
       
    83 #include "ap_config.h"
       
    84 #include "apr_base64.h"
       
    85 #include "apr_lib.h"
       
    86 #include "apr_time.h"
       
    87 #include "apr_errno.h"
       
    88 #include "apr_global_mutex.h"
       
    89 #include "apr_strings.h"
       
    90 #include "ap_compat.h"
       
    91 
       
    92 #include <gssapi/gssapi.h>
       
    93 #include <gssapi/gssapi_ext.h>
       
    94 
       
    95 module auth_gss_module;
       
    96 
       
    97 static void *gss_create_dir_config(apr_pool_t *, char *);
       
    98 
       
    99 int gss_authenticate(request_rec *);
       
   100 
       
   101 typedef struct {
       
   102 	char *gss_service_name;
       
   103 	char *keytab_file;
       
   104 	int gss_debug;
       
   105 } gss_auth_config;
       
   106 
       
   107 static const char *set_service_name(cmd_parms *cmd, void *config,
       
   108 	const char *name)
       
   109 {
       
   110     ((gss_auth_config *) config)->gss_service_name = (char *)name;
       
   111     return NULL;
       
   112 }
       
   113 
       
   114 static const char *set_keytab_file(cmd_parms *cmd, void *config,
       
   115 	const char *file)
       
   116 {
       
   117 	((gss_auth_config *) config)->keytab_file = (char *)file;
       
   118 	return NULL;
       
   119 }
       
   120 
       
   121 static const char *set_gss_debug(cmd_parms *cmd, void *config,
       
   122 	const char *debugflag)
       
   123 {
       
   124 	((gss_auth_config *) config)->gss_debug = atoi(debugflag);
       
   125 	return NULL;
       
   126 }
       
   127 
       
   128 static const command_rec gss_auth_cmds[] = {
       
   129 	AP_INIT_TAKE1("AuthGSSServiceName", set_service_name, NULL,
       
   130 		OR_AUTHCFG, "Service name used for authentication."),
       
   131 
       
   132 	AP_INIT_TAKE1("AuthGSSKeytabFile", set_keytab_file, NULL,
       
   133 		OR_AUTHCFG,
       
   134 		"Location of Kerberos V5 keytab file."),
       
   135 
       
   136 	AP_INIT_TAKE1("AuthGssDebug", set_gss_debug, NULL,
       
   137 		OR_AUTHCFG,
       
   138 		"Enable debug logging in error_log"),
       
   139 	{ NULL }
       
   140 };
       
   141 
       
   142 static void
       
   143 gss_register_hooks(apr_pool_t *p)
       
   144 {
       
   145 	ap_hook_check_user_id(gss_authenticate,NULL,NULL,APR_HOOK_MIDDLE);
       
   146 }
       
   147 
       
   148 module AP_MODULE_DECLARE_DATA auth_gss_module = {
       
   149 	STANDARD20_MODULE_STUFF,
       
   150 	gss_create_dir_config,	/* dir config creater */
       
   151 	NULL,			/* dir merger --- default is to override */
       
   152 	NULL,			/* server config */
       
   153 	NULL,			/* merge server config */
       
   154 	gss_auth_cmds,		/* command apr_table_t */
       
   155 	gss_register_hooks		/* register hooks */
       
   156 };
       
   157 
       
   158 typedef struct {
       
   159 	gss_ctx_id_t context;
       
   160 	gss_cred_id_t server_creds;
       
   161 } gss_connection_t;
       
   162 
       
   163 static gss_connection_t *gss_connection = NULL;
       
   164 
       
   165 static void *
       
   166 gss_create_dir_config(apr_pool_t *p, char *d)
       
   167 {
       
   168 	gss_auth_config *rec =
       
   169 		(gss_auth_config *) apr_pcalloc(p, sizeof(gss_auth_config));
       
   170 
       
   171 	((gss_auth_config *)rec)->gss_service_name = "HTTP";
       
   172 	((gss_auth_config *)rec)->keytab_file = "/var/apache2/http.keytab";
       
   173 	((gss_auth_config *)rec)->gss_debug = 0;
       
   174 
       
   175 	return rec;
       
   176 }
       
   177 
       
   178 void log_rerror(const char *file, int line, int level, int status,
       
   179                 const request_rec *r, const char *fmt, ...)
       
   180 {
       
   181 	char errstr[1024];
       
   182 	va_list ap;
       
   183 
       
   184 	va_start(ap, fmt);
       
   185 	vsnprintf(errstr, sizeof(errstr), fmt, ap);
       
   186 	va_end(ap);
       
   187 
       
   188 	ap_log_rerror(file, line, level | APLOG_NOERRNO, NULL, r, "%s", errstr);
       
   189 }
       
   190 
       
   191 /*********************************************************************
       
   192  * GSSAPI Authentication
       
   193  ********************************************************************/
       
   194 static const char *
       
   195 gss_error_msg(apr_pool_t *p, OM_uint32 maj, OM_uint32 min, char *prefix)
       
   196 {
       
   197 	OM_uint32 maj_stat, min_stat; 
       
   198 	OM_uint32 msg_ctx = 0;
       
   199 	gss_buffer_desc msg;
       
   200 
       
   201 	char *err_msg = (char *)apr_pstrdup(p, prefix);
       
   202 
       
   203 	do {
       
   204 		maj_stat = gss_display_status (&min_stat,
       
   205 			maj, GSS_C_GSS_CODE,
       
   206 			GSS_C_NO_OID, &msg_ctx,
       
   207 			&msg);
       
   208 		if (GSS_ERROR(maj_stat))
       
   209 			break;
       
   210 
       
   211 		err_msg = apr_pstrcat(p, err_msg, ": ", (char*) msg.value,
       
   212 			NULL);
       
   213 		(void) gss_release_buffer(&min_stat, &msg);
       
   214       
       
   215 		maj_stat = gss_display_status (&min_stat,
       
   216 			min, GSS_C_MECH_CODE,
       
   217 			GSS_C_NULL_OID, &msg_ctx,
       
   218 			&msg);
       
   219 		if (!GSS_ERROR(maj_stat)) {
       
   220 			err_msg = apr_pstrcat(p, err_msg,
       
   221 				" (", (char*) msg.value, ")", NULL);
       
   222 			(void) gss_release_buffer(&min_stat, &msg);
       
   223 		}
       
   224 	} while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
       
   225 
       
   226 	return (err_msg);
       
   227 }
       
   228 
       
   229 static int
       
   230 cleanup_gss_connection(void *data)
       
   231 {
       
   232 	OM_uint32 ret;
       
   233 	OM_uint32 minor_status;
       
   234 	gss_connection_t *gss_conn = (gss_connection_t *)data;
       
   235 
       
   236 	if (data == NULL)
       
   237 		return 0; 
       
   238 
       
   239 	if (gss_conn->context != GSS_C_NO_CONTEXT) {
       
   240 		(void) gss_delete_sec_context(&minor_status,
       
   241 			&gss_conn->context,
       
   242 			GSS_C_NO_BUFFER);
       
   243 	}
       
   244 
       
   245 	if (gss_conn->server_creds != GSS_C_NO_CREDENTIAL) {
       
   246 		(void) gss_release_cred(&minor_status, &gss_conn->server_creds);
       
   247 	}
       
   248 
       
   249 	gss_connection = NULL;
       
   250 
       
   251 	return 0; 
       
   252 }
       
   253 
       
   254 static int
       
   255 acquire_server_creds(request_rec *r,
       
   256 	gss_auth_config *conf,
       
   257 	gss_OID_set mechset,
       
   258 	gss_cred_id_t *server_creds)
       
   259 {
       
   260 	int ret = 0;
       
   261 	gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
       
   262 	OM_uint32 major_status, minor_status, minor_status2;
       
   263 	gss_name_t server_name = GSS_C_NO_NAME;
       
   264 	char buf[1024];
       
   265 
       
   266 	snprintf(buf, sizeof(buf), "%s@%s",
       
   267 		conf->gss_service_name, r->hostname);
       
   268 
       
   269 	if (conf->gss_debug)
       
   270    		log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   271 			"acquire_server_creds for %s", buf);
       
   272 
       
   273 	input_token.value = buf;
       
   274 	input_token.length = strlen(buf) + 1;
       
   275 
       
   276 	major_status = gss_import_name(&minor_status, &input_token,
       
   277  			  GSS_C_NT_HOSTBASED_SERVICE,
       
   278 			  &server_name);
       
   279 
       
   280 	if (GSS_ERROR(major_status)) {
       
   281 		log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   282 			"%s", gss_error_msg(r->pool, major_status, minor_status,
       
   283 			"gss_import_name() failed"));
       
   284 		return (HTTP_INTERNAL_SERVER_ERROR);
       
   285 	}
       
   286 
       
   287 	major_status = gss_acquire_cred(&minor_status, server_name,
       
   288 		GSS_C_INDEFINITE,
       
   289 		mechset, GSS_C_ACCEPT,
       
   290 		server_creds, NULL, NULL);
       
   291 
       
   292 	if (GSS_ERROR(major_status)) {
       
   293 		log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   294 			"%s", gss_error_msg(r->pool, major_status, minor_status,
       
   295 		      "gss_acquire_cred() failed"));
       
   296 		ret = HTTP_INTERNAL_SERVER_ERROR;
       
   297 	}
       
   298 	(void) gss_release_name(&minor_status2, &server_name);
       
   299 
       
   300 	return (ret);
       
   301 }
       
   302 
       
   303 static int
       
   304 authenticate_user_gss(request_rec *r, gss_auth_config *conf,
       
   305 	const char *auth_line, char **negotiate_ret_value)
       
   306 {
       
   307 	int ret = 0;
       
   308 	OM_uint32 major_status, minor_status, minor_status2;
       
   309 	gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
       
   310 	gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
       
   311 	const char *auth_param = NULL;
       
   312 	gss_name_t client_name = GSS_C_NO_NAME;
       
   313 	gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
       
   314 
       
   315 	if (conf->gss_debug)
       
   316 		log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   317 		"authenticate_user_gss called");
       
   318 
       
   319 	*negotiate_ret_value = (char *)"";
       
   320 
       
   321 	if (gss_connection == NULL) {
       
   322 		gss_connection = apr_pcalloc(r->connection->pool, sizeof(*gss_connection));
       
   323 		if (gss_connection == NULL) {
       
   324 			log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   325 		           "apr_pcalloc() failed (not enough memory)");
       
   326 			ret = HTTP_INTERNAL_SERVER_ERROR;
       
   327 			goto end;
       
   328 		}
       
   329 		(void) memset(gss_connection, 0, sizeof(*gss_connection));
       
   330 		apr_pool_cleanup_register(r->connection->pool, gss_connection,
       
   331 			cleanup_gss_connection, apr_pool_cleanup_null);
       
   332 	}
       
   333 
       
   334 	if (conf->keytab_file) {
       
   335 		char *ktname;
       
   336 		/*
       
   337 		 * We don't use the ap_* calls here, since the string 
       
   338 		 * passed to putenv() will become part of the enviroment 
       
   339 		 * and shouldn't be free()ed by apache.
       
   340 		 */
       
   341 		ktname = malloc(strlen("KRB5_KTNAME=") + strlen(conf->keytab_file) + 1);
       
   342 		if (ktname == NULL) {
       
   343 			log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
       
   344 				"malloc() failed: not enough memory");
       
   345 			ret = HTTP_INTERNAL_SERVER_ERROR;
       
   346 			goto end;
       
   347 		}
       
   348 		/*
       
   349 		 * Put the keytab name in the environment so that Kerberos
       
   350 		 * knows where to look later.
       
   351 		 */
       
   352 		sprintf(ktname, "KRB5_KTNAME=%s", conf->keytab_file);
       
   353 		putenv(ktname);
       
   354 		if (conf->gss_debug)
       
   355 			log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Using keytab: %s", ktname);
       
   356 	}
       
   357 
       
   358 	/* ap_getword() shifts parameter */
       
   359 	auth_param = ap_getword_white(r->pool, &auth_line);
       
   360 	if (auth_param == NULL) {
       
   361 		log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   362 			"No Authorization parameter in request from client");
       
   363 		ret = HTTP_UNAUTHORIZED;
       
   364 		goto end;
       
   365 	}
       
   366 
       
   367 	input_token.length = apr_base64_decode_len(auth_param) + 1;
       
   368 	input_token.value = apr_pcalloc(r->connection->pool, input_token.length);
       
   369 
       
   370 	if (input_token.value == NULL) {
       
   371 		log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   372 	   	"apr_pcalloc() failed (not enough memory)");
       
   373 		ret = HTTP_INTERNAL_SERVER_ERROR;
       
   374 		goto end;
       
   375 	}
       
   376 	input_token.length = apr_base64_decode(input_token.value, auth_param);
       
   377 
       
   378 	if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
       
   379 		gss_OID_set_desc desiredMechs;
       
   380 		gss_OID_desc client_mech_desc;
       
   381 		gss_OID client_mechoid = &client_mech_desc;
       
   382 		char *mechstr = NULL;
       
   383 
       
   384 		if (!__gss_get_mech_type(client_mechoid, &input_token)) {
       
   385 			mechstr = (char *)__gss_oid_to_mech(client_mechoid);
       
   386 		}
       
   387 		if (mechstr == NULL) {
       
   388 			client_mechoid = GSS_C_NULL_OID;
       
   389 			mechstr = "<unknown>";
       
   390 		}
       
   391 
       
   392 		if (conf->gss_debug)   
       
   393 			log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   394 				"Client wants GSS mech: %s", mechstr);
       
   395 
       
   396 		desiredMechs.count = 1;
       
   397 		desiredMechs.elements = client_mechoid;
       
   398 
       
   399 		/* Get creds using the mechanism that the client requested */
       
   400 		ret = acquire_server_creds(r, conf, &desiredMechs,
       
   401 			&gss_connection->server_creds);
       
   402 		if (ret)
       
   403 			goto end;
       
   404 	} 
       
   405 	/*
       
   406 	 * Try to display the server creds information.
       
   407 	 */
       
   408 	if (conf->gss_debug) {
       
   409 		gss_name_t sname;
       
   410 		gss_buffer_desc dname;
       
   411 
       
   412 		major_status = gss_inquire_cred(&minor_status,
       
   413 			gss_connection->server_creds,
       
   414 			&sname, NULL, NULL, NULL);
       
   415 		if (major_status == GSS_S_COMPLETE) {
       
   416 			major_status = gss_display_name(&minor_status,
       
   417 				sname, &dname, NULL);
       
   418 		}
       
   419 		if (major_status == GSS_S_COMPLETE) {
       
   420 			log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   421 				"got server creds for: %.*s",
       
   422 				(int)dname.length,
       
   423 				(char *)dname.value);
       
   424 			(void) gss_release_name(&minor_status, &sname);
       
   425 			(void) gss_release_buffer(&minor_status, &dname);
       
   426 		}
       
   427 	}
       
   428 
       
   429 	major_status = gss_accept_sec_context(&minor_status,
       
   430 			  &gss_connection->context,
       
   431 			  gss_connection->server_creds,
       
   432 			  &input_token,
       
   433 			  GSS_C_NO_CHANNEL_BINDINGS,
       
   434 			  &client_name,
       
   435 			  NULL,
       
   436 			  &output_token,
       
   437 			  NULL,
       
   438 			  NULL,
       
   439 			  &delegated_cred);
       
   440 
       
   441 	if (output_token.length) {
       
   442 		char *token = NULL;
       
   443 		size_t len;
       
   444 		len = apr_base64_encode_len(output_token.length) + 1;
       
   445 		token = apr_pcalloc(r->connection->pool, len + 1);
       
   446 		if (token == NULL) {
       
   447 			log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   448 			"apr_pcalloc() failed (not enough memory)");
       
   449 			ret = HTTP_INTERNAL_SERVER_ERROR;
       
   450 			gss_release_buffer(&minor_status2, &output_token);
       
   451 			goto end;
       
   452 		}
       
   453 		apr_base64_encode(token, output_token.value, output_token.length);
       
   454 		token[len] = '\0';
       
   455 		*negotiate_ret_value = token;
       
   456 	}
       
   457 
       
   458 	if (GSS_ERROR(major_status)) {
       
   459 		log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   460 			"%s", gss_error_msg(r->pool, major_status, minor_status,
       
   461 			"gss_accept_sec_context() failed"));
       
   462 		/* Don't offer the Negotiate method again if call to GSS layer failed */
       
   463 		*negotiate_ret_value = NULL;
       
   464 		ret = HTTP_UNAUTHORIZED;
       
   465 		goto end;
       
   466 	}
       
   467 
       
   468 	if (major_status == GSS_S_CONTINUE_NEEDED) {
       
   469 		/*
       
   470 		 * Some GSSAPI mechanisms may require multiple iterations to
       
   471 		 * establish authentication.  Most notably, when MUTUAL_AUTHENTICATION
       
   472 		 * flag is used, multiple round trips are needed.
       
   473 		 */
       
   474 		ret = HTTP_UNAUTHORIZED;
       
   475 		goto end;
       
   476 	}
       
   477 
       
   478 	if (client_name != GSS_C_NO_NAME) {
       
   479 		gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
       
   480 		major_status = gss_display_name(&minor_status, client_name,
       
   481 			&name_token, NULL);
       
   482 
       
   483 		if (GSS_ERROR(major_status)) {
       
   484 			log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
       
   485 				"%s", gss_error_msg(r->pool, major_status,
       
   486 				minor_status,
       
   487 				"gss_export_name() failed"));
       
   488 			ret = HTTP_INTERNAL_SERVER_ERROR;
       
   489 			goto end;
       
   490 		}
       
   491 		if (name_token.length)  {
       
   492 			r->user = apr_pstrdup(r->pool, name_token.value);
       
   493 			gss_release_buffer(&minor_status, &name_token);
       
   494 		}
       
   495 
       
   496 		if (conf->gss_debug)
       
   497 			log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   498 				"Authenticated user: %s",
       
   499 			r->user ?  r->user : "<unknown>");
       
   500 	} 
       
   501 	r->ap_auth_type = "Negotiate";
       
   502 	ret = OK;
       
   503 end:
       
   504 	if (delegated_cred)
       
   505 		gss_release_cred(&minor_status, &delegated_cred);
       
   506 
       
   507 	if (output_token.length) 
       
   508 		gss_release_buffer(&minor_status, &output_token);
       
   509 
       
   510 	if (client_name != GSS_C_NO_NAME)
       
   511 		gss_release_name(&minor_status, &client_name);
       
   512 
       
   513 	cleanup_gss_connection(gss_connection);
       
   514 
       
   515 	return ret;
       
   516 }
       
   517 
       
   518 static int
       
   519 already_succeeded(request_rec *r)
       
   520 {
       
   521 	if (ap_is_initial_req(r) || r->ap_auth_type == NULL)
       
   522 		return 0;
       
   523 
       
   524 	return (strcmp(r->ap_auth_type, "Negotiate") ||
       
   525 		(strcmp(r->ap_auth_type, "Basic") && strchr(r->user, '@')));
       
   526 }
       
   527 
       
   528 static void
       
   529 note_gss_auth_failure(request_rec *r, const gss_auth_config *conf,
       
   530 	char *negotiate_ret_value)
       
   531 {
       
   532 	const char *auth_name = NULL;
       
   533 	int set_basic = 0;
       
   534 	char *negoauth_param;
       
   535 
       
   536 	/* get the user realm specified in .htaccess */
       
   537 	auth_name = ap_auth_name(r);
       
   538   
       
   539 	if (conf->gss_debug)
       
   540    		log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   541 			"note_gss_auth_failure: auth_name = %s",
       
   542 			auth_name ? auth_name : "<undefined>");
       
   543 
       
   544 	if (negotiate_ret_value != NULL) {
       
   545 		negoauth_param = (*negotiate_ret_value == '\0') ? "Negotiate" :
       
   546 			apr_pstrcat(r->pool, "Negotiate ", negotiate_ret_value, NULL);
       
   547 		apr_table_add(r->err_headers_out, "WWW-Authenticate", negoauth_param);
       
   548 	}
       
   549 }
       
   550 
       
   551 int
       
   552 gss_authenticate(request_rec *r)
       
   553 {
       
   554 	int ret;
       
   555 	gss_auth_config *conf = 
       
   556 		(gss_auth_config *) ap_get_module_config(r->per_dir_config,
       
   557 			&auth_gss_module);
       
   558 	const char *auth_type = NULL;
       
   559 	const char *auth_line = NULL;
       
   560 	const char *type = NULL;
       
   561 	char *negotiate_ret_value;
       
   562 	static int last_return = HTTP_UNAUTHORIZED;
       
   563 
       
   564 	/* get the type specified in .htaccess */
       
   565 	type = ap_auth_type(r);
       
   566 
       
   567 	if (conf->gss_debug)
       
   568 		log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   569 		"gss_authenticate: type = %s", type);
       
   570 
       
   571 	if (type == NULL || (strcasecmp(type, "GSSAPI") != 0)) {
       
   572 		return DECLINED;
       
   573 	}
       
   574 
       
   575 	/* get what the user sent us in the HTTP header */
       
   576 	auth_line = apr_table_get(r->headers_in, "Authorization");
       
   577 
       
   578 	if (!auth_line) {
       
   579 		if (conf->gss_debug)
       
   580 			log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   581 				"No authentication data found");
       
   582 		note_gss_auth_failure(r, conf, "\0");
       
   583 		return HTTP_UNAUTHORIZED;
       
   584 	}
       
   585 	auth_type = ap_getword_white(r->pool, &auth_line);
       
   586 
       
   587 	if (already_succeeded(r))
       
   588 		return last_return;
       
   589 
       
   590 	if (strcasecmp(auth_type, "Negotiate") == 0) {
       
   591 		ret = authenticate_user_gss(r, conf, auth_line, &negotiate_ret_value);
       
   592 	} else {
       
   593 		ret = HTTP_UNAUTHORIZED;
       
   594 	}
       
   595 
       
   596 	if (ret == HTTP_UNAUTHORIZED) {
       
   597 		if (conf->gss_debug)
       
   598 			log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
       
   599 				"Authentication failed.");
       
   600 		note_gss_auth_failure(r, conf, negotiate_ret_value);
       
   601 	}
       
   602 
       
   603 	last_return = ret;
       
   604 	return ret;
       
   605 }