1 /***************************************************************************
2 Included Headers And Module Declaration
3 ***************************************************************************/
8 #include "http_config.h"
11 #include "http_protocol.h"
12 #include "http_request.h"
14 module kerb_auth_module;
17 #include "apr_strings.h"
19 #include "ap_config.h"
21 #include "http_config.h"
22 #include "http_core.h"
24 #include "http_protocol.h"
25 #include "http_request.h"
27 module AP_MODULE_DECLARE_DATA kerb_auth_module;
42 /***************************************************************************
43 Macros To Ease Compatibility
44 ***************************************************************************/
47 #define MK_TABLE_GET ap_table_get
48 #define MK_TABLE_SET ap_table_set
49 #define MK_TABLE_TYPE table
50 #define MK_PSTRDUP ap_pstrdup
51 #define MK_PROXY STD_PROXY
52 #define MK_USER r->connection->user
53 #define MK_AUTH_TYPE r->connection->ap_auth_type
54 #define MK_ARRAY_HEADER array_header
57 #define MK_POOL apr_pool_t
58 #define MK_TABLE_GET apr_table_get
59 #define MK_TABLE_SET apr_table_set
60 #define MK_TABLE_TYPE apr_table_t
61 #define MK_PSTRDUP apr_pstrdup
62 #define MK_PROXY PROXYREQ_PROXY
63 #define MK_USER r->user
64 #define MK_AUTH_TYPE r->ap_auth_type
65 #define MK_ARRAY_HEADER apr_array_header_t
72 /***************************************************************************
73 Auth Configuration Structure
74 ***************************************************************************/
83 int krb_authoritative;
84 char *krb_default_realm;
86 char *krb_force_instance;
94 int krb_save_credentials;
101 /***************************************************************************
102 Auth Configuration Initialization
103 ***************************************************************************/
104 static void *kerb_dir_config(MK_POOL *p, char *d)
107 rec = (void *) ap_pcalloc(p, sizeof(kerb_auth_config));
108 ((kerb_auth_config *)rec)->krb_fail_status = HTTP_UNAUTHORIZED;
109 ((kerb_auth_config *)rec)->krb_authoritative = 0;
110 ((kerb_auth_config *)rec)->krb_auth_type = MK_PSTRDUP(p, "None");
117 /***************************************************************************
118 Auth Configuration Parsers
119 ***************************************************************************/
120 static const char *kerb_set_fail_slot(cmd_parms *cmd, void *struct_ptr,
123 int offset = (int) (long) cmd->info;
124 if (!strncasecmp(arg, "unauthorized", 12))
125 *(int *) ((char *)struct_ptr + offset) = HTTP_UNAUTHORIZED;
126 else if (!strncasecmp(arg, "forbidden", 9))
127 *(int *) ((char *)struct_ptr + offset) = HTTP_FORBIDDEN;
128 else if (!strncasecmp(arg, "declined", 8))
129 *(int *) ((char *)struct_ptr + offset) = DECLINED;
131 return "KrbAuthFailStatus must be Forbidden, Unauthorized, or Declined.";
135 /* these are either char *struct_ptr, char *arg or void *struct_ptr, const char *arg */
136 static const char *kerb_set_type_slot(cmd_parms *cmd, void *struct_ptr,
139 int offset = (int) (long) cmd->info;
142 (!strncasecmp(arg, "v5", 2))
143 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosV5");
147 (!strncasecmp(arg, "v4", 2))
148 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosV4");
151 (!strncasecmp(arg, "dualv5v4", 8))
152 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosDualV5V4");
154 (!strncasecmp(arg, "dualv4v5", 8))
155 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosDualV4V5");
156 #if defined(KRB4) && defined(KRB5)
157 #endif /* KRB4 && KRB5 */
159 return "AuthKerberos must be V5, V4, DualV4V5, or DualV5V4.";
166 /***************************************************************************
167 Auth Configuration Commands
168 ***************************************************************************/
170 command_rec kerb_auth_cmds[] = {
174 (void*)XtOffsetOf(kerb_auth_config, krb_auth_type),
177 "Permit Kerberos auth without AuthType requirement."
184 (void*)XtOffsetOf(kerb_auth_config, krb_4_srvtab),
185 RSRC_CONF & ACCESS_CONF,
187 "Location of Kerberos V4 srvtab file."
195 (void*)XtOffsetOf(kerb_auth_config, krb_5_keytab),
196 RSRC_CONF & ACCESS_CONF,
198 "Location of Kerberos V5 keytab file."
205 (void*)XtOffsetOf(kerb_auth_config, krb_authoritative),
208 "Refuse to pass request down to lower modules."
214 (void*)XtOffsetOf(kerb_auth_config, krb_default_realm),
217 "Default realm to authenticate users against."
223 (void*)XtOffsetOf(kerb_auth_config, krb_fail_status),
226 "If auth fails, return status set here."
232 (void*)XtOffsetOf(kerb_auth_config, krb_force_instance),
235 "Force authentication against an instance specified here."
242 (void*)XtOffsetOf(kerb_auth_config, krb_forwardable),
245 "Credentials retrieved will be flagged as forwardable."
252 (void*)XtOffsetOf(kerb_auth_config, krb_lifetime),
255 "Lifetime of tickets retrieved."
262 (void*)XtOffsetOf(kerb_auth_config, krb_renewable),
265 "Credentials retrieved will be renewable for this length."
270 "KrbSaveCredentials",
272 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
275 "Save and store credentials/tickets retrieved during auth."
281 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
284 "Alias for KrbSaveCredentials."
290 (void*)XtOffsetOf(kerb_auth_config, krb_tmp_dir),
291 RSRC_CONF & ACCESS_CONF,
293 "Path to store ticket files and such in."
300 static const command_rec kerb_auth_cmds[] = {
304 (void*)APR_XtOffsetOf(kerb_auth_config, krb_auth_type),
306 "Permit Kerberos auth without AuthType requirement."
313 (void*)APR_XtOffsetOf(kerb_auth_config, krb_4_srvtab),
314 RSRC_CONF & ACCESS_CONF,
315 "Location of Kerberos V4 srvtab file."
323 (void*)APR_XtOffsetOf(kerb_auth_config, krb_5_keytab),
324 RSRC_CONF & ACCESS_CONF,
325 "Location of Kerberos V5 keytab file."
332 (void*)APR_XtOffsetOf(kerb_auth_config, krb_authoritative),
334 "Refuse to pass request down to lower modules."
340 (void*)APR_XtOffsetOf(kerb_auth_config, krb_default_realm),
342 "Default realm to authenticate users against."
348 (void*)APR_XtOffsetOf(kerb_auth_config, krb_fail_status),
350 "If auth fails, return status set here."
356 (void*)APR_XtOffsetOf(kerb_auth_config, krb_force_instance),
358 "Force authentication against an instance specified here."
365 (void*)APR_XtOffsetOf(kerb_auth_config, krb_forwardable),
367 "Credentials retrieved will be flagged as forwardable."
374 (void*)APR_XtOffsetOf(kerb_auth_config, krb_lifetime),
376 "Lifetime of tickets retrieved."
383 (void*)APR_XtOffsetOf(kerb_auth_config, krb_renewable),
385 "Credentials retrieved will be renewable for this length."
390 "KrbSaveCredentials",
392 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
394 "Save and store credentials/tickets retrieved during auth."
400 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
402 "Alias for KrbSaveCredentials."
408 (void*)APR_XtOffsetOf(kerb_auth_config, krb_tmp_dir),
409 RSRC_CONF & ACCESS_CONF,
410 "Path to store ticket files and such in."
421 /***************************************************************************
422 Username/Password Validation
423 ***************************************************************************/
425 int kerb5_password_validate(request_rec *r, const char *user, const char *pass)
427 kerb_auth_config *conf =
428 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
431 krb5_context kcontext;
432 krb5_principal server, me;
435 krb5_ccache ccache = NULL;
436 krb5_deltat lifetime = 300; /* 5 minutes */
437 krb5_deltat renewal = 0;
438 krb5_flags options = 0;
439 krb5_data tgtname = {
444 char *c, ccname[MAX_STRING_LEN];
446 if (krb5_init_context(&kcontext))
449 if (conf->krb_save_credentials) {
450 lifetime = 1800; /* 30 minutes */
452 if (conf->krb_forwardable) {
453 options |= KDC_OPT_FORWARDABLE;
456 if (conf->krb_renewable) {
457 options |= KDC_OPT_RENEWABLE;
458 renewal = 86400; /* 24 hours */
461 sprintf(ccname, "FILE:%s/k5cc_ap_%s",
462 conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
465 for (c = ccname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
466 "/tmp") + 1; *c; c++) {
471 ap_table_setn(r->subprocess_env, "KRB5CCNAME", ccname);
472 if (krb5_cc_set_default_name(kcontext, ccname)) {
475 unlink(ccname+strlen("FILE:"));
478 if (conf->krb_lifetime) {
479 lifetime = atoi(conf->krb_lifetime);
482 memset((char *)&my_creds, 0, sizeof(my_creds));
483 if(krb5_parse_name(kcontext, user, &me))
485 my_creds.client = me;
487 if (krb5_build_principal_ext(kcontext, &server,
488 krb5_princ_realm(kcontext, me)->length,
489 krb5_princ_realm(kcontext, me)->data,
490 tgtname.length, tgtname.data,
491 krb5_princ_realm(kcontext, me)->length,
492 krb5_princ_realm(kcontext, me)->data,
496 my_creds.server = server;
497 if (krb5_timeofday(kcontext, &now))
499 my_creds.times.starttime = 0;
500 my_creds.times.endtime = now + lifetime;
501 my_creds.times.renew_till = now + renewal;
503 if (conf->krb_save_credentials) {
504 if (krb5_cc_resolve(kcontext, ccname, &ccache))
507 if (krb5_cc_initialize(kcontext, ccache, me))
511 ret = krb5_get_in_tkt_with_password(kcontext, options, 0, NULL, 0,
512 pass, ccache, &my_creds, 0);
517 krb5_free_cred_contents(kcontext, &my_creds);
524 int kerb4_password_validate(request_rec *r, const char *user, const char *pass)
526 kerb_auth_config *conf =
527 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
530 int lifetime = DEFAULT_TKT_LIFE;
532 char *username = NULL;
533 char *instance = NULL;
536 username = (char *)ap_pstrdup(r->pool, user);
541 instance = strchr(username, '.');
549 realm = strchr(username, '@');
557 if (conf->krb_lifetime) {
558 lifetime = atoi(conf->krb_lifetime);
561 if (conf->krb_force_instance) {
562 instance = conf->krb_force_instance;
565 if (conf->krb_save_credentials) {
566 tfname = (char *)malloc(sizeof(char) * MAX_STRING_LEN);
567 sprintf(tfname, "%s/k5cc_ap_%s",
568 conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
571 if (!strcmp(instance, "")) {
572 tfname = strcat(tfname, ".");
573 tfname = strcat(tfname, instance);
576 if (!strcmp(realm, "")) {
577 tfname = strcat(tfname, ".");
578 tfname = strcat(tfname, realm);
581 for (c = tfname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
582 "/tmp") + 1; *c; c++) {
587 krb_set_tkt_string(tfname);
590 if (!strcmp(realm, "")) {
591 realm = (char *)malloc(sizeof(char) * (REALM_SZ + 1));
592 ret = krb_get_lrealm(realm, 1);
597 ret = krb_get_pw_in_tkt((char *)user, instance, realm, "krbtgt", realm,
598 lifetime, (char *)pass);
615 /***************************************************************************
617 ***************************************************************************/
618 int kerb_authenticate_user(request_rec *r)
620 const char *name; /* AuthName specified */
621 const char *type; /* AuthType specified */
622 int KerberosV5 = 0; /* Kerberos V5 check enabled */
623 int KerberosV4 = 0; /* Kerberos V4 check enabled */
624 int KerberosV4first = 0; /* Kerberos V4 check first */
625 const char *sent_pw; /* Password sent by browser */
626 int res; /* Response holder */
627 int retcode; /* Return code holder */
628 const char *t; /* Decoded auth_line */
629 const char *authtype; /* AuthType to send back to browser */
630 const char *auth_line = MK_TABLE_GET(r->headers_in,
631 (r->proxyreq == MK_PROXY)
632 ? "Proxy-Authorization"
634 kerb_auth_config *conf =
635 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
638 type = ap_auth_type(r);
642 if ((strncasecmp(type, "KerberosV5", 10) == 0) ||
643 (strncasecmp(conf->krb_auth_type, "KerberosV5", 10) == 0)) {
649 if ((strncasecmp(type, "KerberosV4", 10) == 0) ||
650 (strncasecmp(conf->krb_auth_type, "KerberosV4", 10) == 0)) {
655 #if defined(KRB5) && defined(KRB4)
656 if ((strncasecmp(type, "KerberosDualV5V4", 15) == 0) ||
657 (strncasecmp(conf->krb_auth_type, "KerberosDualV5V4", 15) == 0)) {
662 if ((strncasecmp(type, "KerberosDualV4V5", 15) == 0) ||
663 (strncasecmp(conf->krb_auth_type, "KerberosDualV4V5", 15) == 0)) {
668 #endif /* KRB5 && KRB4 */
671 if (!KerberosV4 && !KerberosV5) {
672 if (conf->krb_authoritative) {
673 return HTTP_UNAUTHORIZED;
680 name = ap_auth_name(r);
682 return HTTP_INTERNAL_SERVER_ERROR;
686 MK_TABLE_SET(r->err_headers_out, "WWW-Authenticate",
687 (char *)ap_pstrcat(r->pool,
688 "Basic realm=\"", name, "\"", NULL));
689 return HTTP_UNAUTHORIZED;
692 type = ap_getword_white(r->pool, &auth_line);
693 t = ap_pbase64decode(r->pool, auth_line);
694 MK_USER = ap_getword_nulls(r->pool, &t, ':');
695 MK_AUTH_TYPE = "Kerberos";
696 sent_pw = ap_getword_white(r->pool, &t);
701 if (KerberosV5 && !KerberosV4first && retcode != OK) {
702 MK_AUTH_TYPE = "KerberosV5";
703 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
707 retcode = conf->krb_fail_status;
713 if (KerberosV4 && retcode != OK) {
714 MK_AUTH_TYPE = "KerberosV4";
715 if (kerb4_password_validate(r, MK_USER, sent_pw)) {
719 retcode = conf->krb_fail_status;
724 #if defined(KRB5) && defined(KRB4)
725 if (KerberosV5 && KerberosV4first && retcode != OK) {
726 MK_AUTH_TYPE = "KerberosV5";
727 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
731 retcode = conf->krb_fail_status;
734 #endif /* KRB5 && KRB4 */
736 if (conf->krb_authoritative && retcode == DECLINED) {
737 return HTTP_UNAUTHORIZED;
747 /***************************************************************************
749 ***************************************************************************/
750 int kerb_check_user_access(request_rec *r)
754 const MK_ARRAY_HEADER *reqs_arr = ap_requires(r);
756 kerb_auth_config *conf =
757 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
760 if (reqs_arr == NULL) {
763 reqs = (require_line *)reqs_arr->elts;
765 for (x = 0; x < reqs_arr->nelts; x++) {
766 t = reqs[x].requirement;
767 w = ap_getword_white(r->pool, &t);
768 if (strcmp(w, "realm") == 0) {
769 while (t[0] != '\0') {
770 w = ap_getword_conf(r->pool, &t);
771 if (strcmp(MK_USER, w) == 0) {
784 /***************************************************************************
785 Module Setup/Configuration
786 ***************************************************************************/
788 module MODULE_VAR_EXPORT kerb_auth_module = {
789 STANDARD_MODULE_STUFF,
790 NULL, /* module initializer */
791 kerb_dir_config, /* per-directory config creator */
792 NULL, /* per-directory config merger */
793 NULL, /* per-server config creator */
794 NULL, /* per-server config merger */
795 kerb_auth_cmds, /* command table */
796 NULL, /* [ 9] content handlers */
797 NULL, /* [ 2] URI-to-filename translation */
798 kerb_authenticate_user, /* [ 5] check/validate user_id */
799 kerb_check_user_access, /* [ 6] check user_id is valid *here* */
800 NULL, /* [ 4] check access by host address */
801 NULL, /* [ 7] MIME type checker/setter */
802 NULL, /* [ 8] fixups */
803 NULL, /* [10] logger */
804 NULL, /* [ 3] header parser */
805 NULL, /* process initialization */
806 NULL, /* process exit/cleanup */
807 NULL /* [ 1] post read_request handling */
809 , /* EAPI Additions */
810 NULL, /* EAPI add module */
811 NULL, /* EAPI remove module */
812 NULL, /* EAPI rewrite command */
813 NULL /* EAPI new connection */
818 void kerb_register_hooks(apr_pool_t *p)
820 ap_hook_check_user_id(kerb_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
821 ap_hook_access_checker(kerb_check_user_access, NULL, NULL, APR_HOOK_MIDDLE);
824 module AP_MODULE_DECLARE_DATA kerb_auth_module =
826 STANDARD20_MODULE_STUFF,
827 kerb_dir_config, /* create per-dir conf structures */
828 NULL, /* merge per-dir conf structures */
829 NULL, /* create per-server conf structures */
830 NULL, /* merge per-server conf structures */
831 kerb_auth_cmds, /* table of configuration directives */
832 kerb_register_hooks /* register hooks */