1 /***************************************************************************
2 Included Headers And Module Declaration
3 ***************************************************************************/
6 #include "http_config.h"
9 #include "http_protocol.h"
10 #include "http_request.h"
12 module kerb_auth_module;
15 #include "apr_strings.h"
17 #include "ap_config.h"
19 #include "http_config.h"
20 #include "http_core.h"
22 #include "http_protocol.h"
23 #include "http_request.h"
25 module AP_MODULE_DECLARE_DATA kerb_auth_module;
40 /***************************************************************************
41 Macros To Ease Compatibility
42 ***************************************************************************/
45 #define MK_TABLE_GET ap_table_get
46 #define MK_TABLE_SET ap_table_set
47 #define MK_TABLE_TYPE table
48 #define MK_PSTRDUP ap_pstrdup
49 #define MK_PROXY STD_PROXY
50 #define MK_USER r->connection->user
51 #define MK_AUTH_TYPE r->connection->ap_auth_type
52 #define MK_ARRAY_HEADER array_header
55 #define MK_POOL apr_pool_t
56 #define MK_TABLE_GET apr_table_get
57 #define MK_TABLE_SET apr_table_set
58 #define MK_TABLE_TYPE apr_table_t
59 #define MK_PSTRDUP apr_pstrdup
60 #define MK_PROXY PROXYREQ_PROXY
61 #define MK_USER r->user
62 #define MK_AUTH_TYPE r->ap_auth_type
63 #define MK_ARRAY_HEADER apr_array_header_t
70 /***************************************************************************
71 Auth Configuration Structure
72 ***************************************************************************/
81 int krb_authoritative;
82 char *krb_default_realm;
84 char *krb_force_instance;
92 int krb_save_credentials;
99 /***************************************************************************
100 Auth Configuration Initialization
101 ***************************************************************************/
102 static void *kerb_dir_config(MK_POOL *p, char *d)
105 rec = (void *) ap_pcalloc(p, sizeof(kerb_auth_config));
106 ((kerb_auth_config *)rec)->krb_fail_status = HTTP_UNAUTHORIZED;
107 ((kerb_auth_config *)rec)->krb_authoritative = 0;
108 ((kerb_auth_config *)rec)->krb_auth_type = MK_PSTRDUP(p, "None");
115 /***************************************************************************
116 Auth Configuration Parsers
117 ***************************************************************************/
118 static const char *kerb_set_fail_slot(cmd_parms *cmd, void *struct_ptr,
121 int offset = (int) (long) cmd->info;
122 if (!strncasecmp(arg, "unauthorized", 12))
123 *(int *) ((char *)struct_ptr + offset) = HTTP_UNAUTHORIZED;
124 else if (!strncasecmp(arg, "forbidden", 9))
125 *(int *) ((char *)struct_ptr + offset) = HTTP_FORBIDDEN;
126 else if (!strncasecmp(arg, "declined", 8))
127 *(int *) ((char *)struct_ptr + offset) = DECLINED;
129 return "KrbAuthFailStatus must be Forbidden, Unauthorized, or Declined.";
133 /* these are either char *struct_ptr, char *arg or void *struct_ptr, const char *arg */
134 static const char *kerb_set_type_slot(cmd_parms *cmd, void *struct_ptr,
137 int offset = (int) (long) cmd->info;
140 (!strncasecmp(arg, "v5", 2))
141 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosV5");
145 (!strncasecmp(arg, "v4", 2))
146 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosV4");
149 (!strncasecmp(arg, "dualv5v4", 8))
150 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosDualV5V4");
152 (!strncasecmp(arg, "dualv4v5", 8))
153 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosDualV4V5");
154 #if defined(KRB4) && defined(KRB5)
155 #endif /* KRB4 && KRB5 */
157 return "AuthKerberos must be V5, V4, DualV4V5, or DualV5V4.";
164 /***************************************************************************
165 Auth Configuration Commands
166 ***************************************************************************/
168 command_rec kerb_auth_cmds[] = {
172 (void*)XtOffsetOf(kerb_auth_config, krb_auth_type),
175 "Permit Kerberos auth without AuthType requirement."
182 (void*)XtOffsetOf(kerb_auth_config, krb_4_srvtab),
183 RSRC_CONF & ACCESS_CONF,
185 "Location of Kerberos V4 srvtab file."
193 (void*)XtOffsetOf(kerb_auth_config, krb_5_keytab),
194 RSRC_CONF & ACCESS_CONF,
196 "Location of Kerberos V5 keytab file."
203 (void*)XtOffsetOf(kerb_auth_config, krb_authoritative),
206 "Refuse to pass request down to lower modules."
212 (void*)XtOffsetOf(kerb_auth_config, krb_default_realm),
215 "Default realm to authenticate users against."
221 (void*)XtOffsetOf(kerb_auth_config, krb_fail_status),
224 "If auth fails, return status set here."
230 (void*)XtOffsetOf(kerb_auth_config, krb_force_instance),
233 "Force authentication against an instance specified here."
240 (void*)XtOffsetOf(kerb_auth_config, krb_forwardable),
243 "Credentials retrieved will be flagged as forwardable."
250 (void*)XtOffsetOf(kerb_auth_config, krb_lifetime),
253 "Lifetime of tickets retrieved."
260 (void*)XtOffsetOf(kerb_auth_config, krb_renewable),
263 "Credentials retrieved will be renewable for this length."
268 "KrbSaveCredentials",
270 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
273 "Save and store credentials/tickets retrieved during auth."
279 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
282 "Alias for KrbSaveCredentials."
288 (void*)XtOffsetOf(kerb_auth_config, krb_tmp_dir),
289 RSRC_CONF & ACCESS_CONF,
291 "Path to store ticket files and such in."
298 static const command_rec kerb_auth_cmds[] = {
302 (void*)APR_XtOffsetOf(kerb_auth_config, krb_auth_type),
304 "Permit Kerberos auth without AuthType requirement."
311 (void*)APR_XtOffsetOf(kerb_auth_config, krb_4_srvtab),
312 RSRC_CONF & ACCESS_CONF,
313 "Location of Kerberos V4 srvtab file."
321 (void*)APR_XtOffsetOf(kerb_auth_config, krb_5_keytab),
322 RSRC_CONF & ACCESS_CONF,
323 "Location of Kerberos V5 keytab file."
330 (void*)APR_XtOffsetOf(kerb_auth_config, krb_authoritative),
332 "Refuse to pass request down to lower modules."
338 (void*)APR_XtOffsetOf(kerb_auth_config, krb_default_realm),
340 "Default realm to authenticate users against."
346 (void*)APR_XtOffsetOf(kerb_auth_config, krb_fail_status),
348 "If auth fails, return status set here."
354 (void*)APR_XtOffsetOf(kerb_auth_config, krb_force_instance),
356 "Force authentication against an instance specified here."
363 (void*)APR_XtOffsetOf(kerb_auth_config, krb_forwardable),
365 "Credentials retrieved will be flagged as forwardable."
372 (void*)APR_XtOffsetOf(kerb_auth_config, krb_lifetime),
374 "Lifetime of tickets retrieved."
381 (void*)APR_XtOffsetOf(kerb_auth_config, krb_renewable),
383 "Credentials retrieved will be renewable for this length."
388 "KrbSaveCredentials",
390 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
392 "Save and store credentials/tickets retrieved during auth."
398 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
400 "Alias for KrbSaveCredentials."
406 (void*)APR_XtOffsetOf(kerb_auth_config, krb_tmp_dir),
407 RSRC_CONF & ACCESS_CONF,
408 "Path to store ticket files and such in."
419 /***************************************************************************
420 Username/Password Validation
421 ***************************************************************************/
423 int kerb5_password_validate(request_rec *r, const char *user, const char *pass)
425 kerb_auth_config *conf =
426 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
429 krb5_context kcontext;
430 krb5_principal server, me;
433 krb5_ccache ccache = NULL;
434 krb5_deltat lifetime = 300; /* 5 minutes */
435 krb5_deltat renewal = 0;
436 krb5_flags options = 0;
437 krb5_data tgtname = {
442 char *c, ccname[MAX_STRING_LEN];
444 if (krb5_init_context(&kcontext))
447 if (conf->krb_save_credentials) {
448 lifetime = 1800; /* 30 minutes */
450 if (conf->krb_forwardable) {
451 options |= KDC_OPT_FORWARDABLE;
454 if (conf->krb_renewable) {
455 options |= KDC_OPT_RENEWABLE;
456 renewal = 86400; /* 24 hours */
459 sprintf(ccname, "FILE:%s/k5cc_ap_%s",
460 conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
463 for (c = ccname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
464 "/tmp") + 1; *c; c++) {
469 ap_table_setn(r->subprocess_env, "KRB5CCNAME", ccname);
470 if (krb5_cc_set_default_name(kcontext, ccname)) {
473 unlink(ccname+strlen("FILE:"));
476 if (conf->krb_lifetime) {
477 lifetime = atoi(conf->krb_lifetime);
480 memset((char *)&my_creds, 0, sizeof(my_creds));
481 if(krb5_parse_name(kcontext, user, &me))
483 my_creds.client = me;
485 if (krb5_build_principal_ext(kcontext, &server,
486 krb5_princ_realm(kcontext, me)->length,
487 krb5_princ_realm(kcontext, me)->data,
488 tgtname.length, tgtname.data,
489 krb5_princ_realm(kcontext, me)->length,
490 krb5_princ_realm(kcontext, me)->data,
494 my_creds.server = server;
495 if (krb5_timeofday(kcontext, &now))
497 my_creds.times.starttime = 0;
498 my_creds.times.endtime = now + lifetime;
499 my_creds.times.renew_till = now + renewal;
501 if (conf->krb_save_credentials) {
502 if (krb5_cc_resolve(kcontext, ccname, &ccache))
505 if (krb5_cc_initialize(kcontext, ccache, me))
509 ret = krb5_get_in_tkt_with_password(kcontext, options, 0, NULL, 0,
510 pass, ccache, &my_creds, 0);
515 krb5_free_cred_contents(kcontext, &my_creds);
522 int kerb4_password_validate(request_rec *r, const char *user, const char *pass)
524 kerb_auth_config *conf =
525 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
528 int lifetime = DEFAULT_TKT_LIFE;
530 char *username = NULL;
531 char *instance = NULL;
534 username = (char *)ap_pstrdup(r->pool, user);
539 instance = strchr(username, '.');
547 realm = strchr(username, '@');
555 if (conf->krb_lifetime) {
556 lifetime = atoi(conf->krb_lifetime);
559 if (conf->krb_force_instance) {
560 instance = conf->krb_force_instance;
563 if (conf->krb_save_credentials) {
564 tfname = (char *)malloc(sizeof(char) * MAX_STRING_LEN);
565 sprintf(tfname, "%s/k5cc_ap_%s",
566 conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
569 if (!strcmp(instance, "")) {
570 tfname = strcat(tfname, ".");
571 tfname = strcat(tfname, instance);
574 if (!strcmp(realm, "")) {
575 tfname = strcat(tfname, ".");
576 tfname = strcat(tfname, realm);
579 for (c = tfname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
580 "/tmp") + 1; *c; c++) {
585 krb_set_tkt_string(tfname);
588 if (!strcmp(realm, "")) {
589 realm = (char *)malloc(sizeof(char) * (REALM_SZ + 1));
590 ret = krb_get_lrealm(realm, 1);
595 ret = krb_get_pw_in_tkt((char *)user, instance, realm, "krbtgt", realm,
596 lifetime, (char *)pass);
613 /***************************************************************************
615 ***************************************************************************/
616 int kerb_authenticate_user(request_rec *r)
618 const char *name; /* AuthName specified */
619 const char *type; /* AuthType specified */
620 int KerberosV5 = 0; /* Kerberos V5 check enabled */
621 int KerberosV4 = 0; /* Kerberos V4 check enabled */
622 int KerberosV4first = 0; /* Kerberos V4 check first */
623 const char *sent_pw; /* Password sent by browser */
624 int res; /* Response holder */
625 int retcode; /* Return code holder */
626 const char *t; /* Decoded auth_line */
627 const char *authtype; /* AuthType to send back to browser */
628 const char *auth_line = MK_TABLE_GET(r->headers_in,
629 (r->proxyreq == MK_PROXY)
630 ? "Proxy-Authorization"
632 kerb_auth_config *conf =
633 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
636 type = ap_auth_type(r);
640 if ((strncasecmp(type, "KerberosV5", 10) == 0) ||
641 (strncasecmp(conf->krb_auth_type, "KerberosV5", 10) == 0)) {
647 if ((strncasecmp(type, "KerberosV4", 10) == 0) ||
648 (strncasecmp(conf->krb_auth_type, "KerberosV4", 10) == 0)) {
653 #if defined(KRB5) && defined(KRB4)
654 if ((strncasecmp(type, "KerberosDualV5V4", 15) == 0) ||
655 (strncasecmp(conf->krb_auth_type, "KerberosDualV5V4", 15) == 0)) {
660 if ((strncasecmp(type, "KerberosDualV4V5", 15) == 0) ||
661 (strncasecmp(conf->krb_auth_type, "KerberosDualV4V5", 15) == 0)) {
666 #endif /* KRB5 && KRB4 */
669 if (!KerberosV4 && !KerberosV5) {
670 if (conf->krb_authoritative) {
671 return HTTP_UNAUTHORIZED;
678 name = ap_auth_name(r);
680 return HTTP_INTERNAL_SERVER_ERROR;
684 MK_TABLE_SET(r->err_headers_out, "WWW-Authenticate",
685 (char *)ap_pstrcat(r->pool,
686 "Basic realm=\"", name, "\"", NULL));
687 return HTTP_UNAUTHORIZED;
690 type = ap_getword_white(r->pool, &auth_line);
691 t = ap_pbase64decode(r->pool, auth_line);
692 MK_USER = ap_getword_nulls(r->pool, &t, ':');
693 MK_AUTH_TYPE = "Kerberos";
694 sent_pw = ap_getword_white(r->pool, &t);
699 if (KerberosV5 && !KerberosV4first && retcode != OK) {
700 MK_AUTH_TYPE = "KerberosV5";
701 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
705 retcode = conf->krb_fail_status;
711 if (KerberosV4 && retcode != OK) {
712 MK_AUTH_TYPE = "KerberosV4";
713 if (kerb4_password_validate(r, MK_USER, sent_pw)) {
717 retcode = conf->krb_fail_status;
722 #if defined(KRB5) && defined(KRB4)
723 if (KerberosV5 && KerberosV4first && retcode != OK) {
724 MK_AUTH_TYPE = "KerberosV5";
725 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
729 retcode = conf->krb_fail_status;
732 #endif /* KRB5 && KRB4 */
734 if (conf->krb_authoritative && retcode == DECLINED) {
735 return HTTP_UNAUTHORIZED;
745 /***************************************************************************
747 ***************************************************************************/
748 int kerb_check_user_access(request_rec *r)
752 const MK_ARRAY_HEADER *reqs_arr = ap_requires(r);
754 kerb_auth_config *conf =
755 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
758 if (reqs_arr == NULL) {
761 reqs = (require_line *)reqs_arr->elts;
763 for (x = 0; x < reqs_arr->nelts; x++) {
764 t = reqs[x].requirement;
765 w = ap_getword_white(r->pool, &t);
766 if (strcmp(w, "realm") == 0) {
767 while (t[0] != '\0') {
768 w = ap_getword_conf(r->pool, &t);
769 if (strcmp(MK_USER, w) == 0) {
782 /***************************************************************************
783 Module Setup/Configuration
784 ***************************************************************************/
786 module MODULE_VAR_EXPORT kerb_auth_module = {
787 STANDARD_MODULE_STUFF,
788 NULL, /* module initializer */
789 kerb_dir_config, /* per-directory config creator */
790 NULL, /* per-directory config merger */
791 NULL, /* per-server config creator */
792 NULL, /* per-server config merger */
793 kerb_auth_cmds, /* command table */
794 NULL, /* [ 9] content handlers */
795 NULL, /* [ 2] URI-to-filename translation */
796 kerb_authenticate_user, /* [ 5] check/validate user_id */
797 kerb_check_user_access, /* [ 6] check user_id is valid *here* */
798 NULL, /* [ 4] check access by host address */
799 NULL, /* [ 7] MIME type checker/setter */
800 NULL, /* [ 8] fixups */
801 NULL, /* [10] logger */
802 NULL, /* [ 3] header parser */
803 NULL, /* process initialization */
804 NULL, /* process exit/cleanup */
805 NULL /* [ 1] post read_request handling */
807 , /* EAPI Additions */
808 NULL, /* EAPI add module */
809 NULL, /* EAPI remove module */
810 NULL, /* EAPI rewrite command */
811 NULL /* EAPI new connection */
816 void kerb_register_hooks(apr_pool_t *p)
818 ap_hook_check_user_id(kerb_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
819 ap_hook_access_checker(kerb_check_user_access, NULL, NULL, APR_HOOK_MIDDLE);
822 module AP_MODULE_DECLARE_DATA kerb_auth_module =
824 STANDARD20_MODULE_STUFF,
825 kerb_dir_config, /* create per-dir conf structures */
826 NULL, /* merge per-dir conf structures */
827 NULL, /* create per-server conf structures */
828 NULL, /* merge per-server conf structures */
829 kerb_auth_cmds, /* table of configuration directives */
830 kerb_register_hooks /* register hooks */