gtk+3: fix dependencies for new gnome/accessibility/at-spi2-core
[oi-userland.git] / components / web / apache24 / mod_auth_gss / mod_auth_gss.c
blob8752bda0104ae3cd170a436fe78e69daf1530824
1 /*
2 * Wyllys Ingersoll <wyllys.ingersoll@sun.com>
4 * Based on work by
5 * Daniel Kouril <kouril@users.sourceforge.net>
6 * James E. Robinson, III <james@ncstate.net>
7 * Daniel Henninger <daniel@ncsu.edu>
8 * Ludek Sulak <xsulak@fi.muni.cz>
9 */
11 /* ====================================================================
12 * The Apache Software License, Version 1.1
14 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
15 * reserved.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
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.
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.
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 apache@apache.org.
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.
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 * ====================================================================
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/>.
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.
70 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
74 #include <sys/types.h>
75 #include <strings.h>
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"
92 #include <gssapi/gssapi.h>
93 #include <gssapi/gssapi_ext.h>
95 module auth_gss_module;
97 static void *gss_create_dir_config(apr_pool_t *, char *);
99 int gss_authenticate(request_rec *);
101 typedef struct {
102 char *gss_service_name;
103 char *keytab_file;
104 int gss_debug;
105 } gss_auth_config;
107 static const char *set_service_name(cmd_parms *cmd, void *config,
108 const char *name)
110 ((gss_auth_config *) config)->gss_service_name = (char *)name;
111 return NULL;
114 static const char *set_keytab_file(cmd_parms *cmd, void *config,
115 const char *file)
117 ((gss_auth_config *) config)->keytab_file = (char *)file;
118 return NULL;
121 static const char *set_gss_debug(cmd_parms *cmd, void *config,
122 const char *debugflag)
124 ((gss_auth_config *) config)->gss_debug = atoi(debugflag);
125 return NULL;
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."),
132 AP_INIT_TAKE1("AuthGSSKeytabFile", set_keytab_file, NULL,
133 OR_AUTHCFG,
134 "Location of Kerberos V5 keytab file."),
136 AP_INIT_TAKE1("AuthGssDebug", set_gss_debug, NULL,
137 OR_AUTHCFG,
138 "Enable debug logging in error_log"),
139 { NULL }
142 static void
143 gss_register_hooks(apr_pool_t *p)
145 ap_hook_check_user_id(gss_authenticate,NULL,NULL,APR_HOOK_MIDDLE);
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 */
158 typedef struct {
159 gss_ctx_id_t context;
160 gss_cred_id_t server_creds;
161 } gss_connection_t;
163 static gss_connection_t *gss_connection = NULL;
165 static void *
166 gss_create_dir_config(apr_pool_t *p, char *d)
168 gss_auth_config *rec =
169 (gss_auth_config *) apr_pcalloc(p, sizeof(gss_auth_config));
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;
175 return rec;
178 #define log_rerror ap_log_rerror
180 /*********************************************************************
181 * GSSAPI Authentication
182 ********************************************************************/
183 static const char *
184 gss_error_msg(apr_pool_t *p, OM_uint32 maj, OM_uint32 min, char *prefix)
186 OM_uint32 maj_stat, min_stat;
187 OM_uint32 msg_ctx = 0;
188 gss_buffer_desc msg;
190 char *err_msg = (char *)apr_pstrdup(p, prefix);
192 do {
193 maj_stat = gss_display_status (&min_stat,
194 maj, GSS_C_GSS_CODE,
195 GSS_C_NO_OID, &msg_ctx,
196 &msg);
197 if (GSS_ERROR(maj_stat))
198 break;
200 err_msg = apr_pstrcat(p, err_msg, ": ", (char*) msg.value,
201 NULL);
202 (void) gss_release_buffer(&min_stat, &msg);
204 maj_stat = gss_display_status (&min_stat,
205 min, GSS_C_MECH_CODE,
206 GSS_C_NULL_OID, &msg_ctx,
207 &msg);
208 if (!GSS_ERROR(maj_stat)) {
209 err_msg = apr_pstrcat(p, err_msg,
210 " (", (char*) msg.value, ")", NULL);
211 (void) gss_release_buffer(&min_stat, &msg);
213 } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
215 return (err_msg);
218 static int
219 cleanup_gss_connection(void *data)
221 OM_uint32 ret;
222 OM_uint32 minor_status;
223 gss_connection_t *gss_conn = (gss_connection_t *)data;
225 if (data == NULL)
226 return 0;
228 if (gss_conn->context != GSS_C_NO_CONTEXT) {
229 (void) gss_delete_sec_context(&minor_status,
230 &gss_conn->context,
231 GSS_C_NO_BUFFER);
234 if (gss_conn->server_creds != GSS_C_NO_CREDENTIAL) {
235 (void) gss_release_cred(&minor_status, &gss_conn->server_creds);
238 gss_connection = NULL;
240 return 0;
243 static int
244 acquire_server_creds(request_rec *r,
245 gss_auth_config *conf,
246 gss_OID_set mechset,
247 gss_cred_id_t *server_creds)
249 int ret = 0;
250 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
251 OM_uint32 major_status, minor_status, minor_status2;
252 gss_name_t server_name = GSS_C_NO_NAME;
253 char buf[1024];
255 snprintf(buf, sizeof(buf), "%s@%s",
256 conf->gss_service_name, r->hostname);
258 if (conf->gss_debug)
259 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
260 "acquire_server_creds for %s", buf);
262 input_token.value = buf;
263 input_token.length = strlen(buf) + 1;
265 major_status = gss_import_name(&minor_status, &input_token,
266 GSS_C_NT_HOSTBASED_SERVICE,
267 &server_name);
269 if (GSS_ERROR(major_status)) {
270 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
271 "%s", gss_error_msg(r->pool, major_status, minor_status,
272 "gss_import_name() failed"));
273 return (HTTP_INTERNAL_SERVER_ERROR);
276 major_status = gss_acquire_cred(&minor_status, server_name,
277 GSS_C_INDEFINITE,
278 mechset, GSS_C_ACCEPT,
279 server_creds, NULL, NULL);
281 if (GSS_ERROR(major_status)) {
282 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
283 "%s", gss_error_msg(r->pool, major_status, minor_status,
284 "gss_acquire_cred() failed"));
285 ret = HTTP_INTERNAL_SERVER_ERROR;
287 (void) gss_release_name(&minor_status2, &server_name);
289 return (ret);
292 static int
293 authenticate_user_gss(request_rec *r, gss_auth_config *conf,
294 const char *auth_line, char **negotiate_ret_value)
296 int ret = 0;
297 OM_uint32 major_status, minor_status, minor_status2;
298 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
299 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
300 const char *auth_param = NULL;
301 gss_name_t client_name = GSS_C_NO_NAME;
302 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
304 if (conf->gss_debug)
305 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
306 "authenticate_user_gss called");
308 *negotiate_ret_value = (char *)"";
310 if (gss_connection == NULL) {
311 gss_connection = apr_pcalloc(r->connection->pool, sizeof(*gss_connection));
312 if (gss_connection == NULL) {
313 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
314 "apr_pcalloc() failed (not enough memory)");
315 ret = HTTP_INTERNAL_SERVER_ERROR;
316 goto end;
318 (void) memset(gss_connection, 0, sizeof(*gss_connection));
319 apr_pool_cleanup_register(r->connection->pool, gss_connection,
320 cleanup_gss_connection, apr_pool_cleanup_null);
323 if (conf->keytab_file) {
324 char *ktname;
326 * We don't use the ap_* calls here, since the string
327 * passed to putenv() will become part of the enviroment
328 * and shouldn't be free()ed by apache.
330 ktname = malloc(strlen("KRB5_KTNAME=") + strlen(conf->keytab_file) + 1);
331 if (ktname == NULL) {
332 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
333 "malloc() failed: not enough memory");
334 ret = HTTP_INTERNAL_SERVER_ERROR;
335 goto end;
338 * Put the keytab name in the environment so that Kerberos
339 * knows where to look later.
341 sprintf(ktname, "KRB5_KTNAME=%s", conf->keytab_file);
342 putenv(ktname);
343 if (conf->gss_debug)
344 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Using keytab: %s", ktname);
347 /* ap_getword() shifts parameter */
348 auth_param = ap_getword_white(r->pool, &auth_line);
349 if (auth_param == NULL) {
350 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
351 "No Authorization parameter in request from client");
352 ret = HTTP_UNAUTHORIZED;
353 goto end;
356 input_token.length = apr_base64_decode_len(auth_param) + 1;
357 input_token.value = apr_pcalloc(r->connection->pool, input_token.length);
359 if (input_token.value == NULL) {
360 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
361 "apr_pcalloc() failed (not enough memory)");
362 ret = HTTP_INTERNAL_SERVER_ERROR;
363 goto end;
365 input_token.length = apr_base64_decode(input_token.value, auth_param);
367 if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
368 gss_OID_set_desc desiredMechs;
369 gss_OID_desc client_mech_desc;
370 gss_OID client_mechoid = &client_mech_desc;
371 char *mechstr = NULL;
373 if (!__gss_get_mech_type(client_mechoid, &input_token)) {
374 mechstr = (char *)__gss_oid_to_mech(client_mechoid);
376 if (mechstr == NULL) {
377 client_mechoid = GSS_C_NULL_OID;
378 mechstr = "<unknown>";
381 if (conf->gss_debug)
382 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
383 "Client wants GSS mech: %s", mechstr);
385 desiredMechs.count = 1;
386 desiredMechs.elements = client_mechoid;
388 /* Get creds using the mechanism that the client requested */
389 ret = acquire_server_creds(r, conf, &desiredMechs,
390 &gss_connection->server_creds);
391 if (ret)
392 goto end;
395 * Try to display the server creds information.
397 if (conf->gss_debug) {
398 gss_name_t sname;
399 gss_buffer_desc dname;
401 major_status = gss_inquire_cred(&minor_status,
402 gss_connection->server_creds,
403 &sname, NULL, NULL, NULL);
404 if (major_status == GSS_S_COMPLETE) {
405 major_status = gss_display_name(&minor_status,
406 sname, &dname, NULL);
408 if (major_status == GSS_S_COMPLETE) {
409 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
410 "got server creds for: %.*s",
411 (int)dname.length,
412 (char *)dname.value);
413 (void) gss_release_name(&minor_status, &sname);
414 (void) gss_release_buffer(&minor_status, &dname);
418 major_status = gss_accept_sec_context(&minor_status,
419 &gss_connection->context,
420 gss_connection->server_creds,
421 &input_token,
422 GSS_C_NO_CHANNEL_BINDINGS,
423 &client_name,
424 NULL,
425 &output_token,
426 NULL,
427 NULL,
428 &delegated_cred);
430 if (output_token.length) {
431 char *token = NULL;
432 size_t len;
433 len = apr_base64_encode_len(output_token.length) + 1;
434 token = apr_pcalloc(r->connection->pool, len + 1);
435 if (token == NULL) {
436 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
437 "apr_pcalloc() failed (not enough memory)");
438 ret = HTTP_INTERNAL_SERVER_ERROR;
439 gss_release_buffer(&minor_status2, &output_token);
440 goto end;
442 apr_base64_encode(token, output_token.value, output_token.length);
443 token[len] = '\0';
444 *negotiate_ret_value = token;
447 if (GSS_ERROR(major_status)) {
448 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
449 "%s", gss_error_msg(r->pool, major_status, minor_status,
450 "gss_accept_sec_context() failed"));
451 /* Don't offer the Negotiate method again if call to GSS layer failed */
452 *negotiate_ret_value = NULL;
453 ret = HTTP_UNAUTHORIZED;
454 goto end;
457 if (major_status == GSS_S_CONTINUE_NEEDED) {
459 * Some GSSAPI mechanisms may require multiple iterations to
460 * establish authentication. Most notably, when MUTUAL_AUTHENTICATION
461 * flag is used, multiple round trips are needed.
463 ret = HTTP_UNAUTHORIZED;
464 goto end;
467 if (client_name != GSS_C_NO_NAME) {
468 gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
469 major_status = gss_display_name(&minor_status, client_name,
470 &name_token, NULL);
472 if (GSS_ERROR(major_status)) {
473 log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
474 "%s", gss_error_msg(r->pool, major_status,
475 minor_status,
476 "gss_export_name() failed"));
477 ret = HTTP_INTERNAL_SERVER_ERROR;
478 goto end;
480 if (name_token.length) {
481 r->user = apr_pstrdup(r->pool, name_token.value);
482 gss_release_buffer(&minor_status, &name_token);
485 if (conf->gss_debug)
486 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
487 "Authenticated user: %s",
488 r->user ? r->user : "<unknown>");
490 r->ap_auth_type = "Negotiate";
491 ret = OK;
492 end:
493 if (delegated_cred)
494 gss_release_cred(&minor_status, &delegated_cred);
496 if (output_token.length)
497 gss_release_buffer(&minor_status, &output_token);
499 if (client_name != GSS_C_NO_NAME)
500 gss_release_name(&minor_status, &client_name);
502 cleanup_gss_connection(gss_connection);
504 return ret;
507 static int
508 already_succeeded(request_rec *r)
510 if (ap_is_initial_req(r) || r->ap_auth_type == NULL)
511 return 0;
513 return (strcmp(r->ap_auth_type, "Negotiate") ||
514 (strcmp(r->ap_auth_type, "Basic") && strchr(r->user, '@')));
517 static void
518 note_gss_auth_failure(request_rec *r, const gss_auth_config *conf,
519 char *negotiate_ret_value)
521 const char *auth_name = NULL;
522 int set_basic = 0;
523 char *negoauth_param;
525 /* get the user realm specified in .htaccess */
526 auth_name = ap_auth_name(r);
528 if (conf->gss_debug)
529 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
530 "note_gss_auth_failure: auth_name = %s",
531 auth_name ? auth_name : "<undefined>");
533 if (negotiate_ret_value != NULL) {
534 negoauth_param = (*negotiate_ret_value == '\0') ? "Negotiate" :
535 apr_pstrcat(r->pool, "Negotiate ", negotiate_ret_value, NULL);
536 apr_table_add(r->err_headers_out, "WWW-Authenticate", negoauth_param);
541 gss_authenticate(request_rec *r)
543 int ret;
544 gss_auth_config *conf =
545 (gss_auth_config *) ap_get_module_config(r->per_dir_config,
546 &auth_gss_module);
547 const char *auth_type = NULL;
548 const char *auth_line = NULL;
549 const char *type = NULL;
550 char *negotiate_ret_value;
551 static int last_return = HTTP_UNAUTHORIZED;
553 /* get the type specified in .htaccess */
554 type = ap_auth_type(r);
556 if (conf->gss_debug)
557 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
558 "gss_authenticate: type = %s", type);
560 if (type == NULL || (strcasecmp(type, "GSSAPI") != 0)) {
561 return DECLINED;
564 /* get what the user sent us in the HTTP header */
565 auth_line = apr_table_get(r->headers_in, "Authorization");
567 if (!auth_line) {
568 if (conf->gss_debug)
569 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
570 "No authentication data found");
571 note_gss_auth_failure(r, conf, "\0");
572 return HTTP_UNAUTHORIZED;
574 auth_type = ap_getword_white(r->pool, &auth_line);
576 if (already_succeeded(r))
577 return last_return;
579 if (strcasecmp(auth_type, "Negotiate") == 0) {
580 ret = authenticate_user_gss(r, conf, auth_line, &negotiate_ret_value);
581 } else {
582 ret = HTTP_UNAUTHORIZED;
585 if (ret == HTTP_UNAUTHORIZED) {
586 if (conf->gss_debug)
587 log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
588 "Authentication failed.");
589 note_gss_auth_failure(r, conf, negotiate_ret_value);
592 last_return = ret;
593 return ret;