4 #include "http_config.h"
7 #include "http_protocol.h"
8 #include "http_request.h"
11 #include "apr_strings.h"
13 #include "ap_config.h"
26 module kerb_auth_module;
28 module AP_MODULE_DECLARE_DATA kerb_auth_module;
32 /***************************************************************************
33 Macros To Ease Compatibility
34 ***************************************************************************/
37 #define MK_TABLE_GET ap_table_get
38 #define MK_TABLE_SET ap_table_set
39 #define MK_TABLE_TYPE table
40 #define MK_PSTRDUP ap_pstrdup
41 #define MK_PROXY STD_PROXY
42 #define MK_USER r->connection->user
43 #define MK_AUTH_TYPE r->connection->ap_auth_type
44 #define MK_ARRAY_HEADER array_header
46 #define MK_POOL apr_pool_t
47 #define MK_TABLE_GET apr_table_get
48 #define MK_TABLE_SET apr_table_set
49 #define MK_TABLE_TYPE apr_table_t
50 #define MK_PSTRDUP apr_pstrdup
51 #define MK_PROXY PROXYREQ_PROXY
52 #define MK_USER r->user
53 #define MK_AUTH_TYPE r->ap_auth_type
54 #define MK_ARRAY_HEADER apr_array_header_t
60 /***************************************************************************
61 Auth Configuration Structure
62 ***************************************************************************/
65 char *krb_auth_realms;
67 char *krb_force_instance;
68 int krb_save_credentials;
75 int krb_method_gssapi;
76 int krb_method_k5pass;
80 int krb_method_k4pass;
85 static const command_rec kerb_auth_cmds[] = {
86 { "AuthKerberos", ap_set_flag_slot,
87 (void*)XtOffsetOf(kerb_auth_config, krb_auth_enable),
88 OR_AUTHCFG, FLAG, "Permit Kerberos auth without AuthType requirement." },
90 { "KrbAuthRealm", ap_set_string_slot,
91 (void*)XtOffsetOf(kerb_auth_config, krb_auth_realms),
92 OR_AUTHCFG, ITERATE, "Realms to attempt authentication against (can be multiple)." },
94 { "KrbAuthRealms", ap_set_string_slot,
95 (void*)XtOffsetOf(kerb_auth_config, krb_auth_realms),
96 OR_AUTHCFG, ITERATE, "Alias for KrbAuthRealm." },
99 { "KrbFailStatus", kerb_set_fail_slot,
100 (void*)XtOffsetOf(kerb_auth_config, krb_fail_status),
101 OR_AUTHCFG, TAKE1, "If auth fails, return status set here." },
104 { "KrbForceInstance", ap_set_string_slot,
105 (void*)XtOffsetOf(kerb_auth_config, krb_force_instance),
106 OR_AUTHCFG, TAKE1, "Force authentication against an instance specified here." },
108 { "KrbSaveCredentials", ap_set_flag_slot,
109 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
110 OR_AUTHCFG, FLAG, "Save and store credentials/tickets retrieved during auth." },
112 { "KrbSaveTickets", ap_set_flag_slot,
113 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
114 OR_AUTHCFG, FLAG, "Alias for KrbSaveCredentials." },
116 { "KrbTmpdir", ap_set_string_slot,
117 (void*)XtOffsetOf(kerb_auth_config, krb_tmp_dir),
118 OR_AUTHCFG, TAKE1, "Path to store ticket files and such in." },
120 { "KrbServiceName", ap_set_string_slot,
121 (void*)XtOffsetOf(kerb_auth_config, service_name),
122 OR_AUTHCFG, TAKE1, "Kerberos service name to be used by apache." },
125 { "KrbLifetime", ap_set_string_slot,
126 (void*)XtOffsetOf(kerb_auth_config, krb_lifetime),
127 OR_AUTHCFG, TAKE1, "Kerberos ticket lifetime." },
131 { "Krb5Keytab", ap_set_file_slot,
132 (void*)XtOffsetOf(kerb_auth_config, krb_5_keytab),
133 OR_AUTHCFG, TAKE1, "Location of Kerberos V5 keytab file." },
135 { "KrbForwardable", ap_set_flag_slot,
136 (void*)XtOffsetOf(kerb_auth_config, krb_forwardable),
137 OR_AUTHCFG, FLAG, "Credentials retrieved will be flagged as forwardable."},
139 { "KrbMethodGSSAPI", ap_set_flag_slot,
140 (void*)XtOffsetOf(kerb_auth_config, krb_method_gssapi),
141 OR_AUTHCFG, FLAG, "Enable GSSAPI authentication." },
143 { "KrbMethodK5Pass", ap_set_flag_slot,
144 (void*)XtOffsetOf(kerb_auth_config, krb_method_k5pass),
145 OR_AUTHCFG, FLAG, "Enable Kerberos V5 password authentication." },
149 { "Krb4Srvtab", ap_set_file_slot,
150 (void*)XtOffsetOf(kerb_auth_config, krb_4_srvtab),
151 OR_AUTHCFG, TAKE1, "Location of Kerberos V4 srvtab file." },
153 { "KrbMethodK4Pass", ap_set_flag_slot,
154 (void*)XtOffsetOf(kerb_auth_config, krb_method_k4pass),
155 OR_AUTHCFG, FLAG, "Enable Kerberos V4 password authentication." },
161 static const command_rec kerb_auth_cmds[] = {
162 AP_INIT_FLAG("AuthKerberos", ap_set_flag_slot,
163 (void*)APR_XtOffsetOf(kerb_auth_config, krb_auth_enable),
164 OR_AUTHCFG, "Permit Kerberos auth without AuthType requirement."),
169 AP_INIT_TAKE1("Krb4Srvtab", ap_set_file_slot,
170 (void*)APR_XtOffsetOf(kerb_auth_config, krb_4_srvtab),
171 OR_AUTHCFG, "Location of Kerberos V4 srvtab file."),
175 AP_INIT_TAKE1("Krb5Keytab", ap_set_file_slot,
176 (void*)APR_XtOffsetOf(kerb_auth_config, krb_5_keytab),
177 OR_AUTHCFG, "Location of Kerberos V5 keytab file."),
181 AP_INIT_FLAG("KrbAuthoritative", ap_set_flag_slot,
182 (void*)APR_XtOffsetOf(kerb_auth_config, krb_authoritative),
183 OR_AUTHCFG, "Refuse to pass request down to lower modules."),
186 AP_INIT_ITERATE("KrbAuthRealm", ap_set_string_slot,
187 (void*)APR_XtOffsetOf(kerb_auth_config, krb_auth_realms),
188 OR_AUTHCFG, "Realm to attempt authentication against (can be multiple)."),
191 AP_INIT_TAKE1("KrbFailStatus", kerb_set_fail_slot,
192 (void*)APR_XtOffsetOf(kerb_auth_config, krb_fail_status),
193 OR_AUTHCFG, "If auth fails, return status set here."),
196 AP_INIT_TAKE1("KrbForceInstance", ap_set_string_slot,
197 (void*)APR_XtOffsetOf(kerb_auth_config, krb_force_instance),
198 OR_AUTHCFG, "Force authentication against an instance specified here."),
201 AP_INIT_FLAG("KrbForwardable", ap_set_flag_slot,
202 (void*)APR_XtOffsetOf(kerb_auth_config, krb_forwardable),
203 OR_AUTHCFG, "Credentials retrieved will be flagged as forwardable."),
206 AP_INIT_TAKE1("KrbLifetime", ap_set_string_slot,
207 (void*)APR_XtOffsetOf(kerb_auth_config, krb_lifetime),
208 OR_AUTHCFG, "Lifetime of tickets retrieved."),
211 AP_INIT_FLAG("KrbMethodGSSAPI", ap_set_flag_slot,
212 (void*)APR_XtOffsetOf(kerb_auth_config, krb_method_gssapi),
213 OR_AUTHCFG, "Enable GSSAPI authentication."),
217 AP_INIT_FLAG("KrbMethodK4Pass", ap_set_flag_slot,
218 (void*)APR_XtOffsetOf(kerb_auth_config, krb_method_k4pass),
219 OR_AUTHCFG, "Enable Kerberos V4 password authentication."),
223 AP_INIT_FLAG("KrbMethodK5Pass", ap_set_flag_slot,
224 (void*)APR_XtOffsetOf(kerb_auth_config, krb_method_k5pass),
225 OR_AUTHCFG, "Enable Kerberos V5 password authentication."),
227 AP_INIT_TAKE1("KrbRenewable", ap_set_string_slot,
228 (void*)APR_XtOffsetOf(kerb_auth_config, krb_renewable),
229 OR_AUTHCFG, "Credentials retrieved will be renewable for this length."),
232 AP_INIT_FLAG("KrbSaveCredentials", ap_set_flag_slot,
233 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
234 OR_AUTHCFG, "Save and store credentials/tickets retrieved during auth."),
236 AP_INIT_FLAG("KrbSaveTickets", ap_set_flag_slot,
237 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
238 OR_AUTHCFG, "Alias for KrbSaveCredentials."),
240 AP_INIT_TAKE1("KrbTmpdir", ap_set_string_slot,
241 (void*)APR_XtOffsetOf(kerb_auth_config, krb_tmp_dir),
242 OR_AUTHCFG, "Path to store ticket files and such in."),
248 /***************************************************************************
249 GSSAPI Support Initialization
250 ***************************************************************************/
253 gss_ctx_id_t context;
254 gss_cred_id_t server_creds;
257 static gss_connection_t *gss_connection = NULL;
260 cleanup_gss_connection(void *data)
262 OM_uint32 minor_status;
263 gss_connection_t *gss_conn = (gss_connection_t *)data;
267 if (gss_conn->context != GSS_C_NO_CONTEXT)
268 gss_delete_sec_context(&minor_status, &gss_conn->context,
270 if (gss_conn->server_creds != GSS_C_NO_CREDENTIAL)
271 gss_release_cred(&minor_status, &gss_conn->server_creds);
278 /***************************************************************************
279 Auth Configuration Initialization
280 ***************************************************************************/
281 static void *kerb_dir_create_config(MK_POOL *p, char *d)
283 kerb_auth_config *rec;
285 rec = (kerb_auth_config *) ap_pcalloc(p, sizeof(kerb_auth_config));
286 ((kerb_auth_config *)rec)->krb_auth_enable = 1;
287 ((kerb_auth_config *)rec)->krb_fail_status = HTTP_UNAUTHORIZED;
289 ((kerb_auth_config *)rec)->krb_method_k5pass = 1;
290 ((kerb_auth_config *)rec)->krb_method_gssapi = 1;
293 ((kerb_auth_config *)rec)->krb_method_k4pass = 1;
301 /***************************************************************************
302 Auth Configuration Parsers
303 ***************************************************************************/
304 static const char *kerb_set_fail_slot(cmd_parms *cmd, void *struct_ptr,
307 int offset = (int) (long) cmd->info;
308 if (!strncasecmp(arg, "unauthorized", 12))
309 *(int *) ((char *)struct_ptr + offset) = HTTP_UNAUTHORIZED;
310 else if (!strncasecmp(arg, "forbidden", 9))
311 *(int *) ((char *)struct_ptr + offset) = HTTP_FORBIDDEN;
312 else if (!strncasecmp(arg, "declined", 8))
313 *(int *) ((char *)struct_ptr + offset) = DECLINED;
315 return "KrbAuthFailStatus must be Forbidden, Unauthorized, or Declined.";
322 krb5_verify_user(krb5_context context, krb5_principal principal,
323 krb5_ccache ccache, const char *password, krb5_boolean secure,
327 krb5_context kcontext;
328 krb5_principal server, client;
331 krb5_flags options = 0;
332 krb5_principal me = NULL;
333 krb5_data tgtname = {
339 memset((char *)&my_creds, 0, sizeof(my_creds));
340 my_creds.client = principal;
342 if (krb5_build_principal_ext(kcontext, &server,
343 krb5_princ_realm(kcontext, me)->length,
344 krb5_princ_realm(kcontext, me)->data,
345 tgtname.length, tgtname.data,
346 krb5_princ_realm(kcontext, me)->length,
347 krb5_princ_realm(kcontext, me)->data,
352 my_creds.server = server;
353 if (krb5_timeofday(kcontext, &now))
356 my_creds.times.starttime = 0;
358 my_creds.times.endtime = now + lifetime;
359 my_creds.times.renew_till = now + renewal;
362 ret = krb5_get_in_tkt_with_password(kcontext, options, 0, NULL, 0,
363 password, ccache, &my_creds, 0);
373 /***************************************************************************
374 Username/Password Validation
375 ***************************************************************************/
378 krb5_cache_cleanup(void *data)
380 krb5_context context;
382 krb5_error_code problem;
383 char *cache_name = (char *) data;
385 problem = krb5_init_context(&context);
387 ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "krb5_init_context() failed");
391 problem = krb5_cc_resolve(context, cache_name, &cache);
393 ap_log_error(APLOG_MARK, APLOG_ERR, NULL,
394 "krb5_cc_resolve() failed (%s: %s)",
395 cache_name, krb5_get_err_text(context, problem));
399 krb5_cc_destroy(context, cache);
400 krb5_free_context(context);
404 create_krb5_ccache(krb5_context kcontext,
406 kerb_auth_config *conf,
407 krb5_principal princ,
410 char *c, ccname[MAX_STRING_LEN];
411 krb5_error_code problem;
414 krb5_ccache tmp_ccache = NULL;
416 snprintf(ccname, sizeof(ccname), "FILE:%s/k5cc_ap_%s",
417 conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
420 for (c = ccname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
421 "/tmp") + 1; *c; c++) {
427 /* not sure what's the purpose of this call here */
428 problem = krb5_cc_set_default_name(kcontext, ccname);
430 snprintf(errstr, sizeof(errstr),
431 "krb5_cc_set_default_name() failed: %s",
432 krb5_get_err_text(kcontext, problem));
433 ap_log_reason (errstr, r->uri, r);
441 /* XXX Dan: Why is this done? Cleanup? But the file would not be
442 * accessible from another processes (CGI) */
443 unlink(ccname+strlen("FILE:"));
446 problem = krb5_cc_resolve(kcontext, ccname, &tmp_ccache);
448 snprintf(errstr, sizeof(errstr),
449 "krb5_cc_resolve() failed: %s",
450 krb5_get_err_text(kcontext, problem));
451 ap_log_reason (errstr, r->uri, r);
456 problem = krb5_cc_initialize(kcontext, tmp_ccache, princ);
458 snprintf(errstr, sizeof(errstr),
459 "krb5_cc_initialize() failed: %s",
460 krb5_get_err_text(kcontext, problem));
461 ap_log_reason (errstr, r->uri, r);
466 ap_table_setn(r->subprocess_env, "KRB5CCNAME", ccname);
467 ap_register_cleanup(r->pool, ccname,
468 krb5_cache_cleanup, ap_null_cleanup);
470 *ccache = tmp_ccache;
477 krb5_cc_destroy(kcontext, tmp_ccache);
479 return ret; /* XXX */
483 store_krb5_creds(krb5_context kcontext,
485 kerb_auth_config *conf,
486 krb5_ccache delegated_cred)
489 krb5_error_code problem;
490 krb5_principal princ;
494 problem = krb5_cc_get_principal(kcontext, delegated_cred, &princ);
496 snprintf(errstr, sizeof(errstr), "krb5_cc_get_principal() failed: %s",
497 krb5_get_err_text(kcontext, problem));
501 ret = create_krb5_ccache(kcontext, r, conf, princ, &ccache);
503 krb5_free_principal(kcontext, princ);
507 problem = krb5_cc_copy_cache(kcontext, delegated_cred, ccache);
508 krb5_free_principal(kcontext, princ);
510 snprintf(errstr, sizeof(errstr), "krb5_cc_copy_cache() failed: %s",
511 krb5_get_err_text(kcontext, problem));
512 krb5_cc_destroy(kcontext, ccache);
516 krb5_cc_close(kcontext, ccache);
520 int authenticate_user_krb5pwd(request_rec *r,
521 kerb_auth_config *conf,
522 const char *auth_line)
524 const char *sent_pw = NULL;
525 const char *realms = NULL;
526 krb5_context kcontext;
527 krb5_error_code code;
528 krb5_principal client = NULL;
529 krb5_ccache ccache = NULL;
532 code = krb5_init_context(&kcontext);
534 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO, r,
535 "Cannot initialize Kerberos5 context (%d)", code);
539 sent_pw = ap_uudecode(r->pool, auth_line);
540 r->connection->user = ap_getword (r->pool, &sent_pw, ':');
541 r->connection->ap_auth_type = "Basic";
543 /* do not allow user to override realm setting of server */
544 if (strchr(r->connection->user,'@')) {
545 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO, r,
546 "specifying realm in user name is prohibited");
547 ret = HTTP_UNAUTHORIZED;
552 code = krb5_cc_gen_new(kcontext, &krb5_mcc_ops, &ccache);
554 code = krb5_mcc_generate_new(kcontext, &ccache);
557 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO, r,
558 "Cannot generate new ccache: %s",
559 krb5_get_err_text(kcontext, code));
564 realms = conf->krb_auth_realms;
566 if (realms && krb5_set_default_realm(kcontext,
567 ap_getword_white(r->pool, &realms)))
570 code = krb5_parse_name(kcontext, r->connection->user, &client);
574 code = krb5_verify_user(kcontext, client, ccache, sent_pw, 1, "khttp");
575 krb5_free_principal(kcontext, client);
579 /* ap_getword_white() used above shifts the parameter, so it's not
580 needed to touch the realms variable */
581 } while (realms && *realms);
583 memset((char *)sent_pw, 0, strlen(sent_pw));
586 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO, r,
587 "Verifying krb5 password failed: %s",
588 krb5_get_err_text(kcontext, code));
589 ret = HTTP_UNAUTHORIZED;
593 if (conf->krb_save_credentials) {
594 ret = store_krb5_creds(kcontext, r, conf, ccache);
595 if (ret) /* Ignore error ?? */
603 krb5_free_principal(kcontext, client);
605 krb5_cc_destroy(kcontext, ccache);
606 krb5_free_context(kcontext);
613 int kerb4_password_validate(request_rec *r, const char *user, const char *pass)
615 kerb_auth_config *conf =
616 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
619 int lifetime = DEFAULT_TKT_LIFE;
621 char *username = NULL;
622 char *instance = NULL;
625 username = (char *)ap_pstrdup(r->pool, user);
630 instance = strchr(username, '.');
638 realm = strchr(username, '@');
646 if (conf->krb_lifetime) {
647 lifetime = atoi(conf->krb_lifetime);
650 if (conf->krb_force_instance) {
651 instance = conf->krb_force_instance;
654 if (conf->krb_save_credentials) {
655 tfname = (char *)malloc(sizeof(char) * MAX_STRING_LEN);
656 sprintf(tfname, "%s/k5cc_ap_%s",
657 conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
660 if (!strcmp(instance, "")) {
661 tfname = strcat(tfname, ".");
662 tfname = strcat(tfname, instance);
665 if (!strcmp(realm, "")) {
666 tfname = strcat(tfname, ".");
667 tfname = strcat(tfname, realm);
670 for (c = tfname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
671 "/tmp") + 1; *c; c++) {
676 krb_set_tkt_string(tfname);
679 if (!strcmp(realm, "")) {
680 realm = (char *)malloc(sizeof(char) * (REALM_SZ + 1));
681 ret = krb_get_lrealm(realm, 1);
686 ret = krb_get_pw_in_tkt((char *)user, instance, realm, "krbtgt", realm,
687 lifetime, (char *)pass);
704 /***************************************************************************
706 ***************************************************************************/
709 get_gss_error(pool *p, OM_uint32 error_status, char *prefix)
711 OM_uint32 maj_stat, min_stat;
712 OM_uint32 msg_ctx = 0;
713 gss_buffer_desc status_string;
717 snprintf(buf, sizeof(buf), "%s: ", prefix);
720 maj_stat = gss_display_status (&min_stat,
726 if (sizeof(buf) > len + status_string.length + 1) {
727 sprintf(buf+len, "%s:", (char*) status_string.value);
728 len += status_string.length;
730 gss_release_buffer(&min_stat, &status_string);
731 } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
733 return (ap_pstrdup(p, buf));
737 get_gss_creds(request_rec *r,
738 kerb_auth_config *conf,
739 gss_cred_id_t *server_creds)
742 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
743 OM_uint32 major_status, minor_status;
744 gss_name_t server_name = GSS_C_NO_NAME;
746 if (conf->service_name) {
747 input_token.value = conf->service_name;
748 input_token.length = strlen(conf->service_name) + 1;
751 input_token.value = "khttp";
752 input_token.length = 6;
754 major_status = gss_import_name(&minor_status, &input_token,
755 (conf->service_name) ?
756 GSS_C_NT_USER_NAME : GSS_C_NT_HOSTBASED_SERVICE,
758 if (GSS_ERROR(major_status)) {
759 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
760 "%s", get_gss_error(r->pool, minor_status,
761 "gss_import_name() failed"));
767 if (conf->krb_5_keytab)
768 setenv("KRB5_KTNAME", conf->krb_5_keytab, 1);
771 major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
772 GSS_C_NO_OID_SET, GSS_C_ACCEPT,
773 server_creds, NULL, NULL);
775 if (conf->krb_5_keytab)
776 unsetenv("KRB5_KTNAME");
778 if (GSS_ERROR(major_status)) {
779 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
780 "%s", get_gss_error(r->pool, minor_status,
781 "gss_acquire_cred() failed"));
795 authenticate_user_gss(request_rec *r,
796 kerb_auth_config *conf,
797 const char *auth_line)
799 OM_uint32 major_status, minor_status, minor_status2;
800 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
801 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
802 const char *auth_param = NULL;
804 gss_name_t client_name = GSS_C_NO_NAME;
805 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
807 if (gss_connection == NULL) {
808 gss_connection = ap_pcalloc(r->connection->pool, sizeof(*gss_connection));
809 if (gss_connection == NULL) {
810 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
811 "ap_pcalloc() failed (not enough memory)");
815 memset(gss_connection, 0, sizeof(*gss_connection));
816 ap_register_cleanup(r->connection->pool, gss_connection, cleanup_gss_connection, ap_null_cleanup);
819 if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
820 ret = get_gss_creds(r, conf, &gss_connection->server_creds);
825 /* ap_getword() shifts parameter */
826 auth_param = ap_getword_white(r->pool, &auth_line);
827 if (auth_param == NULL) {
828 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
829 "No Authorization parameter in request from client");
830 ret = HTTP_UNAUTHORIZED;
834 input_token.length = ap_base64decode_len(auth_param);
835 input_token.value = ap_pcalloc(r->connection->pool, input_token.length);
836 if (input_token.value == NULL) {
837 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
838 "ap_pcalloc() failed (not enough memory)");
842 input_token.length = ap_base64decode(input_token.value, auth_param);
844 major_status = gss_accept_sec_context(&minor_status,
845 &gss_connection->context,
846 gss_connection->server_creds,
848 GSS_C_NO_CHANNEL_BINDINGS,
855 if (output_token.length) {
859 len = ap_base64encode_len(output_token.length);
860 token = ap_pcalloc(r->connection->pool, len + 1);
862 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
863 "ap_pcalloc() failed (not enough memory)");
865 gss_release_buffer(&minor_status2, &output_token);
868 ap_base64encode(token, output_token.value, output_token.length);
870 ap_table_set(r->err_headers_out, "WWW-Authenticate",
871 ap_pstrcat(r->pool, "GSS-Negotiate ", token, NULL));
872 gss_release_buffer(&minor_status2, &output_token);
875 if (GSS_ERROR(major_status)) {
876 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
877 "%s", get_gss_error(r->pool, minor_status,
878 "gss_accept_sec_context() failed"));
879 ret = HTTP_UNAUTHORIZED;
883 if (major_status & GSS_S_CONTINUE_NEEDED) {
884 /* Some GSSAPI mechanism (eg GSI from Globus) may require multiple
885 * iterations to establish authentication */
886 ret = HTTP_UNAUTHORIZED;
890 major_status = gss_export_name(&minor_status, client_name, &output_token);
891 gss_release_name(&minor_status, &client_name);
892 if (GSS_ERROR(major_status)) {
893 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
894 "%s", get_gss_error(r->pool, minor_status,
895 "gss_export_name() failed"));
900 r->connection->ap_auth_type = "Negotiate";
901 r->connection->user = ap_pstrdup(r->pool, output_token.value);
903 /* If the user comes from a realm specified by configuration don't include
904 its realm name in the username so that the authorization routine could
905 work for both Password-based and Ticket-based authentication. It's
906 administrators responsibility to include only such realm that have
907 unified principal instances, i.e. if the same principal name occures in
908 multiple realms, it must be always assigned to a single user.
910 p = strchr(r->connection->user, '@');
912 const char *realms = conf->gss_krb5_realms;
914 while (realms && *realms) {
915 if (strcmp(p+1, ap_getword_white(r->pool, &realms)) == 0) {
923 gss_release_buffer(&minor_status, &output_token);
926 /* This should be only done if afs token are requested or gss_save creds is
928 /* gss_export_cred() from the GGF GSS Extensions could be used */
929 if (delegated_cred != GSS_C_NO_CREDENTIAL &&
930 (conf->gss_save_creds || (conf->gss_krb5_cells && k_hasafs()))) {
931 krb5_init_context(&krb_ctx);
932 do_afs_log(krb_ctx, r, delegated_cred->ccache, conf->gss_krb5_cells);
933 ret = store_krb5_creds(krb_ctx, r, conf, delegated_cred->ccache);
934 krb5_free_context(krb_ctx);
943 gss_release_cred(&minor_status, &delegated_cred);
945 if (output_token.length)
946 gss_release_buffer(&minor_status, &output_token);
948 if (client_name != GSS_C_NO_NAME)
949 gss_release_name(&minor_status, &client_name);
957 note_auth_failure(request_rec *r, const kerb_auth_config *conf)
959 const char *auth_type = NULL;
960 const char *auth_name = NULL;
962 /* get the type specified in .htaccess */
963 auth_type = ap_auth_type(r);
965 /* get the user realm specified in .htaccess */
966 auth_name = ap_auth_name(r);
968 /* XXX should the WWW-Authenticate header be cleared first? */
970 if (conf->krb_method_gssapi)
971 ap_table_add(r->err_headers_out, "WWW-Authenticate", "GSS-Negotiate ");
973 if (auth_type && strncasecmp(auth_type, "KerberosV5", 10) == 0)
974 ap_table_add(r->err_headers_out, "WWW-Authenticate",
975 ap_pstrcat(r->pool, "Basic realm=\"", auth_name, "\"", NULL));
980 /***************************************************************************
982 ***************************************************************************/
983 int kerb_authenticate_user(request_rec *r)
985 kerb_auth_config *conf =
986 (kerb_auth_config *) ap_get_module_config(r->per_dir_config,
988 const char *auth_type = NULL;
989 const char *auth_line = NULL;
990 const char *type = NULL;
993 /* get the type specified in .htaccess */
994 type = ap_auth_type(r);
996 if (!conf->krb_auth_enable &&
997 (type == NULL || (strncasecmp(type, "Kerberos", 8) != 0)))
1000 /* get what the user sent us in the HTTP header */
1001 auth_line = MK_TABLE_GET(r->headers_in, "Authorization");
1003 note_auth_failure(r, conf);
1004 return HTTP_UNAUTHORIZED;
1006 auth_type = ap_getword_white(r->pool, &auth_line);
1008 ret = HTTP_UNAUTHORIZED;
1010 /* XXX Support for AuthType=Kerberos */
1013 if (conf->krb_method_gssapi &&
1014 strcasecmp(auth_type, "GSS-Negotiate") == 0) {
1015 ret = authenticate_user_gss(r, conf, auth_line);
1016 } else if (conf->krb_method_k5pass &&
1017 strcasecmp(auth_type, "Basic") == 0) {
1018 ret = authenticate_user_krb5pwd(r, conf, auth_line);
1023 if (ret == HTTP_UNAUTHORIZED && conf->krb_method_k4pass &&
1024 strcasecmp(auth_type, "Basic") == 0)
1025 ret = authenticate_user_krb4pwd(r, conf, auth_line);
1028 if (ret == HTTP_UNAUTHORIZED)
1029 note_auth_failure(r, conf);
1036 int kerb_check_user_access(request_rec *r)
1040 const MK_ARRAY_HEADER *reqs_arr = ap_requires(r);
1042 kerb_auth_config *conf =
1043 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
1046 if (reqs_arr == NULL) {
1049 reqs = (require_line *)reqs_arr->elts;
1051 for (x = 0; x < reqs_arr->nelts; x++) {
1052 t = reqs[x].requirement;
1053 w = ap_getword_white(r->pool, &t);
1054 if (strcmp(w, "realm") == 0) {
1055 while (t[0] != '\0') {
1056 w = ap_getword_conf(r->pool, &t);
1057 if (strcmp(MK_USER, w) == 0) {
1071 /***************************************************************************
1072 Module Setup/Configuration
1073 ***************************************************************************/
1075 module MODULE_VAR_EXPORT kerb_auth_module = {
1076 STANDARD_MODULE_STUFF,
1077 NULL, /* module initializer */
1078 kerb_dir_create_config, /* per-directory config creator */
1079 NULL, /* per-directory config merger */
1080 NULL, /* per-server config creator */
1081 NULL, /* per-server config merger */
1082 kerb_auth_cmds, /* command table */
1083 NULL, /* [ 9] content handlers */
1084 NULL, /* [ 2] URI-to-filename translation */
1085 kerb_authenticate_user, /* [ 5] check/validate user_id */
1086 NULL, /* [ 6] check user_id is valid *here* */
1087 NULL, /* [ 4] check access by host address */
1088 NULL, /* [ 7] MIME type checker/setter */
1089 NULL, /* [ 8] fixups */
1090 NULL, /* [10] logger */
1091 NULL, /* [ 3] header parser */
1092 NULL, /* process initialization */
1093 NULL, /* process exit/cleanup */
1094 NULL /* [ 1] post read_request handling */
1097 void kerb_register_hooks(apr_pool_t *p)
1099 ap_hook_check_user_id(kerb_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
1102 module AP_MODULE_DECLARE_DATA kerb_auth_module =
1104 STANDARD20_MODULE_STUFF,
1105 kerb_dir_create_config, /* create per-dir conf structures */
1106 NULL, /* merge per-dir conf structures */
1107 NULL, /* create per-server conf structures */
1108 NULL, /* merge per-server conf structures */
1109 kerb_auth_cmds, /* table of configuration directives */
1110 kerb_register_hooks /* register hooks */