- Added functions for Negotiate support, not called from the body yet.
[mod_auth_kerb.cvs/.git] / src / mod_auth_kerb.c
1 /*************************************************************************** 
2  Included Headers And Module Declaration
3  ***************************************************************************/
4 #ident "$Id$"
5
6 #ifdef APXS1
7 #include "httpd.h"
8 #include "http_config.h"
9 #include "http_core.h"
10 #include "http_log.h"
11 #include "http_protocol.h"
12 #include "http_request.h"
13
14 module kerb_auth_module;
15 #else
16 #ifdef APXS2
17 #include "apr_strings.h"
18 #include "apr_lib.h"
19 #include "ap_config.h"
20 #include "httpd.h"
21 #include "http_config.h"
22 #include "http_core.h"
23 #include "http_log.h"
24 #include "http_protocol.h"
25 #include "http_request.h"
26
27 module AP_MODULE_DECLARE_DATA kerb_auth_module;
28 #endif /* APXS2 */
29 #endif /* APXS1 */
30
31 #ifdef KRB5
32 #include <krb5.h>
33 #include <gssapi.h>
34 #endif /* KRB5 */
35
36 #ifdef KRB4
37 #include <krb.h>
38 #endif /* KRB4 */
39
40
41
42
43 /*************************************************************************** 
44  Macros To Ease Compatibility
45  ***************************************************************************/
46 #ifdef APXS1
47 #define MK_POOL pool
48 #define MK_TABLE_GET ap_table_get
49 #define MK_TABLE_SET ap_table_set
50 #define MK_TABLE_TYPE table
51 #define MK_PSTRDUP ap_pstrdup
52 #define MK_PROXY STD_PROXY
53 #define MK_USER r->connection->user
54 #define MK_AUTH_TYPE r->connection->ap_auth_type
55 #define MK_ARRAY_HEADER array_header
56 #else
57 #ifdef APXS2
58 #define MK_POOL apr_pool_t
59 #define MK_TABLE_GET apr_table_get
60 #define MK_TABLE_SET apr_table_set
61 #define MK_TABLE_TYPE apr_table_t
62 #define MK_PSTRDUP apr_pstrdup
63 #define MK_PROXY PROXYREQ_PROXY
64 #define MK_USER r->user
65 #define MK_AUTH_TYPE r->ap_auth_type
66 #define MK_ARRAY_HEADER apr_array_header_t
67 #endif /* APXS2 */
68 #endif /* APXS1 */
69
70
71
72
73 /*************************************************************************** 
74  Auth Configuration Structure
75  ***************************************************************************/
76 typedef struct {
77         char *krb_auth_type;
78 #ifdef KRB4
79         char *krb_4_srvtab;
80 #endif /* KRB4 */
81 #ifdef KRB5
82         char *krb_5_keytab;
83 #endif /* KRB5 */
84         int krb_authoritative;
85         char *krb_default_realm;
86         int krb_fail_status;
87         char *krb_force_instance;
88 #ifdef KRB5
89         int krb_forwardable;
90 #endif /* KRB5 */
91         char *krb_lifetime;
92 #ifdef KRB5
93         char *krb_renewable;
94 #endif /* KRB5 */
95         int krb_save_credentials;
96         char *krb_tmp_dir;
97         char *service_name;
98 } kerb_auth_config;
99
100 typedef struct {
101    gss_ctx_id_t context;
102    gss_cred_id_t server_creds;
103 } gss_connection_t;
104
105 static gss_connection_t *gss_connection = NULL;
106
107 static void
108 cleanup_gss_connection(void *data)
109 {
110    OM_uint32 minor_status;
111    gss_connection_t *gss_conn = (gss_connection_t *)data;
112
113    if (data == NULL)
114       return;
115    if (gss_conn->context != GSS_C_NO_CONTEXT)
116       gss_delete_sec_context(&minor_status, &gss_conn->context,
117                              GSS_C_NO_BUFFER);
118    if (gss_conn->server_creds != GSS_C_NO_CREDENTIAL)
119       gss_release_cred(&minor_status, &gss_conn->server_creds);
120 }
121
122
123
124 /*************************************************************************** 
125  Auth Configuration Initialization
126  ***************************************************************************/
127 static void *kerb_dir_config(MK_POOL *p, char *d)
128 {
129         static void *rec;
130         rec = (void *) ap_pcalloc(p, sizeof(kerb_auth_config));
131         ((kerb_auth_config *)rec)->krb_fail_status = HTTP_UNAUTHORIZED;
132         ((kerb_auth_config *)rec)->krb_authoritative = 0;
133         ((kerb_auth_config *)rec)->krb_auth_type = MK_PSTRDUP(p, "None");
134         return rec;
135 }
136
137
138
139
140 /*************************************************************************** 
141  Auth Configuration Parsers
142  ***************************************************************************/
143 static const char *kerb_set_fail_slot(cmd_parms *cmd, void *struct_ptr,
144                                         const char *arg)
145 {
146         int offset = (int) (long) cmd->info;
147         if (!strncasecmp(arg, "unauthorized", 12))
148                 *(int *) ((char *)struct_ptr + offset) = HTTP_UNAUTHORIZED;
149         else if (!strncasecmp(arg, "forbidden", 9))
150                 *(int *) ((char *)struct_ptr + offset) = HTTP_FORBIDDEN;
151         else if (!strncasecmp(arg, "declined", 8))
152                 *(int *) ((char *)struct_ptr + offset) = DECLINED;
153         else
154                 return "KrbAuthFailStatus must be Forbidden, Unauthorized, or Declined.";
155         return NULL;
156 }
157
158 /* these are either char *struct_ptr, char *arg or void *struct_ptr, const char *arg */
159 static const char *kerb_set_type_slot(cmd_parms *cmd, void *struct_ptr,
160                                         const char *arg)
161 {
162         int offset = (int) (long) cmd->info;
163 #ifdef KRB5
164         if (!strncasecmp(arg, "v5", 2))
165                 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosV5");
166         else
167 #endif /* KRB5 */
168 #ifdef KRB4
169         if (!strncasecmp(arg, "v4", 2))
170                 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosV4");
171         else
172 #endif /* KRB4 */
173         if (!strncasecmp(arg, "dualv5v4", 8))
174                 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosDualV5V4");
175         else if
176            (!strncasecmp(arg, "dualv4v5", 8))
177                 *(char **) ((char *)struct_ptr + offset) = MK_PSTRDUP(cmd->pool, "KerberosDualV4V5");
178 #if defined(KRB4) && defined(KRB5)
179 #endif /* KRB4 && KRB5 */
180         else
181                 return "AuthKerberos must be V5, V4, DualV4V5, or DualV5V4.";
182         return NULL;
183 }
184
185
186
187
188 /*************************************************************************** 
189  Auth Configuration Commands
190  ***************************************************************************/
191 #ifdef APXS1
192 command_rec kerb_auth_cmds[] = {
193         {
194                 "AuthKerberos",
195                 kerb_set_type_slot,
196                 (void*)XtOffsetOf(kerb_auth_config, krb_auth_type),
197                 OR_AUTHCFG,
198                 TAKE1,
199                 "Permit Kerberos auth without AuthType requirement."
200         },
201
202 #ifdef KRB4
203         {
204                 "Krb4Srvtab",
205                 ap_set_file_slot,
206                 (void*)XtOffsetOf(kerb_auth_config, krb_4_srvtab),
207                 RSRC_CONF & ACCESS_CONF,
208                 TAKE1,
209                 "Location of Kerberos V4 srvtab file."
210         },
211 #endif /* KRB4 */
212
213 #ifdef KRB5
214         {
215                 "Krb5Keytab",
216                 ap_set_file_slot,
217                 (void*)XtOffsetOf(kerb_auth_config, krb_5_keytab),
218                 RSRC_CONF & ACCESS_CONF,
219                 TAKE1,
220                 "Location of Kerberos V5 keytab file."
221         },
222 #endif /* KRB5 */
223
224         {
225                 "KrbAuthoritative",
226                 ap_set_flag_slot,
227                 (void*)XtOffsetOf(kerb_auth_config, krb_authoritative),
228                 OR_AUTHCFG,
229                 FLAG,
230                 "Refuse to pass request down to lower modules."
231         },
232
233         {
234                 "KrbDefaultRealm",
235                 ap_set_string_slot,
236                 (void*)XtOffsetOf(kerb_auth_config, krb_default_realm),
237                 OR_AUTHCFG,
238                 TAKE1,
239                 "Default realm to authenticate users against."
240         },
241
242         {
243                 "KrbFailStatus",
244                 kerb_set_fail_slot,
245                 (void*)XtOffsetOf(kerb_auth_config, krb_fail_status),
246                 OR_AUTHCFG,
247                 TAKE1,
248                 "If auth fails, return status set here."
249         },
250
251         {
252                 "KrbForceInstance",
253                 ap_set_string_slot,
254                 (void*)XtOffsetOf(kerb_auth_config, krb_force_instance),
255                 OR_AUTHCFG,
256                 TAKE1,
257                 "Force authentication against an instance specified here."
258         },
259
260 #ifdef KRB5
261         {
262                 "KrbForwardable",
263                 ap_set_flag_slot,
264                 (void*)XtOffsetOf(kerb_auth_config, krb_forwardable),
265                 OR_AUTHCFG,
266                 FLAG,
267                 "Credentials retrieved will be flagged as forwardable."
268         },
269 #endif /* KRB5 */
270
271         {
272                 "KrbLifetime",
273                 ap_set_string_slot,
274                 (void*)XtOffsetOf(kerb_auth_config, krb_lifetime),
275                 OR_AUTHCFG,
276                 TAKE1,
277                 "Lifetime of tickets retrieved."
278         },
279
280 #ifdef KRB5
281         {
282                 "KrbRenewable",
283                 ap_set_string_slot,
284                 (void*)XtOffsetOf(kerb_auth_config, krb_renewable),
285                 OR_AUTHCFG,
286                 TAKE1,
287                 "Credentials retrieved will be renewable for this length."
288         },
289 #endif /* KRB5 */
290
291         {
292                 "KrbSaveCredentials",
293                 ap_set_flag_slot,
294                 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
295                 OR_AUTHCFG,
296                 FLAG,
297                 "Save and store credentials/tickets retrieved during auth."
298         },
299
300         {
301                 "KrbSaveTickets",
302                 ap_set_flag_slot,
303                 (void*)XtOffsetOf(kerb_auth_config, krb_save_credentials),
304                 OR_AUTHCFG,
305                 FLAG,
306                 "Alias for KrbSaveCredentials."
307         },
308
309         {
310                 "KrbTmpdir",
311                 ap_set_string_slot,
312                 (void*)XtOffsetOf(kerb_auth_config, krb_tmp_dir),
313                 RSRC_CONF & ACCESS_CONF,
314                 TAKE1,
315                 "Path to store ticket files and such in."
316         },
317
318         { NULL }
319 };
320 #else
321 #ifdef APXS2
322 static const command_rec kerb_auth_cmds[] = {
323         AP_INIT_TAKE1(
324                 "AuthKerberos",
325                 kerb_set_type_slot,
326                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_auth_type),
327                 OR_AUTHCFG,
328                 "Permit Kerberos auth without AuthType requirement."
329         ),
330
331 #ifdef KRB4
332         AP_INIT_TAKE1(
333                 "Krb4Srvtab",
334                 ap_set_file_slot,
335                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_4_srvtab),
336                 RSRC_CONF & ACCESS_CONF,
337                 "Location of Kerberos V4 srvtab file."
338         ),
339 #endif /* KRB4 */
340
341 #ifdef KRB5
342         AP_INIT_TAKE1(
343                 "Krb5Keytab",
344                 ap_set_file_slot,
345                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_5_keytab),
346                 RSRC_CONF & ACCESS_CONF,
347                 "Location of Kerberos V5 keytab file."
348         ),
349 #endif /* KRB5 */
350
351         AP_INIT_FLAG(
352                 "KrbAuthoritative",
353                 ap_set_flag_slot,
354                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_authoritative),
355                 OR_AUTHCFG,
356                 "Refuse to pass request down to lower modules."
357         ),
358
359         AP_INIT_TAKE1(
360                 "KrbDefaultRealm",
361                 ap_set_string_slot,
362                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_default_realm),
363                 OR_AUTHCFG,
364                 "Default realm to authenticate users against."
365         ),
366
367         AP_INIT_TAKE1(
368                 "KrbFailStatus",
369                 kerb_set_fail_slot,
370                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_fail_status),
371                 OR_AUTHCFG,
372                 "If auth fails, return status set here."
373         ),
374
375         AP_INIT_TAKE1(
376                 "KrbForceInstance",
377                 ap_set_string_slot,
378                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_force_instance),
379                 OR_AUTHCFG,
380                 "Force authentication against an instance specified here."
381         ),
382
383 #ifdef KRB5
384         AP_INIT_FLAG(
385                 "KrbForwardable",
386                 ap_set_flag_slot,
387                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_forwardable),
388                 OR_AUTHCFG,
389                 "Credentials retrieved will be flagged as forwardable."
390         ),
391 #endif /* KRB5 */
392
393         AP_INIT_TAKE1(
394                 "KrbLifetime",
395                 ap_set_string_slot,
396                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_lifetime),
397                 OR_AUTHCFG,
398                 "Lifetime of tickets retrieved."
399         ),
400
401 #ifdef KRB5
402         AP_INIT_TAKE1(
403                 "KrbRenewable",
404                 ap_set_string_slot,
405                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_renewable),
406                 OR_AUTHCFG,
407                 "Credentials retrieved will be renewable for this length."
408         ),
409 #endif /* KRB5 */
410
411         AP_INIT_FLAG(
412                 "KrbSaveCredentials",
413                 ap_set_flag_slot,
414                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
415                 OR_AUTHCFG,
416                 "Save and store credentials/tickets retrieved during auth."
417         ),
418
419         AP_INIT_FLAG(
420                 "KrbSaveTickets",
421                 ap_set_flag_slot,
422                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_save_credentials),
423                 OR_AUTHCFG,
424                 "Alias for KrbSaveCredentials."
425         ),
426
427         AP_INIT_TAKE1(
428                 "KrbTmpdir",
429                 ap_set_string_slot,
430                 (void*)APR_XtOffsetOf(kerb_auth_config, krb_tmp_dir),
431                 RSRC_CONF & ACCESS_CONF,
432                 "Path to store ticket files and such in."
433         ),
434
435         { NULL }
436 };
437 #endif /* APXS2 */
438 #endif /* APXS1 */
439
440
441
442
443 /*************************************************************************** 
444  Username/Password Validation
445  ***************************************************************************/
446 #ifdef KRB5
447 int kerb5_password_validate(request_rec *r, const char *user, const char *pass)
448 {
449         kerb_auth_config *conf =
450                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
451                                         &kerb_auth_module);
452         int ret;
453         krb5_context kcontext;
454         krb5_principal server, me;
455         krb5_creds my_creds;
456         krb5_timestamp now;
457         krb5_ccache ccache = NULL;
458         krb5_deltat lifetime = 300;     /* 5 minutes */
459         krb5_deltat renewal = 0;
460         krb5_flags options = 0;
461         krb5_data tgtname = {
462 #ifndef HEIMDAL
463                 0,
464 #endif
465                 KRB5_TGS_NAME_SIZE,
466                 KRB5_TGS_NAME
467         };
468         char *c, ccname[MAX_STRING_LEN];
469
470         if (krb5_init_context(&kcontext))
471                 return 0;
472
473         if (conf->krb_save_credentials) {
474                 lifetime = 1800;        /* 30 minutes */
475
476                 if (conf->krb_forwardable) {
477                         options |= KDC_OPT_FORWARDABLE;
478                 }
479
480                 if (conf->krb_renewable) {
481                         options |= KDC_OPT_RENEWABLE;
482                         renewal = 86400;        /* 24 hours */
483                 }
484
485                 sprintf(ccname, "FILE:%s/k5cc_ap_%s",
486                         conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
487                         MK_USER);
488
489                 for (c = ccname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
490                                 "/tmp") + 1; *c; c++) {
491                         if (*c == '/')
492                                 *c = '.';
493                 }
494
495                 ap_table_setn(r->subprocess_env, "KRB5CCNAME", ccname);
496                 if (krb5_cc_set_default_name(kcontext, ccname)) {
497                         return 0;
498                 }
499                 unlink(ccname+strlen("FILE:"));
500         }
501
502         if (conf->krb_lifetime) {
503                 lifetime = atoi(conf->krb_lifetime);
504         }
505
506         memset((char *)&my_creds, 0, sizeof(my_creds));
507         if(krb5_parse_name(kcontext, user, &me))
508                 return 0;
509         my_creds.client = me;
510
511 #ifdef HEIMDAL
512 #else
513         if (krb5_build_principal_ext(kcontext, &server,
514                                 krb5_princ_realm(kcontext, me)->length,
515                                 krb5_princ_realm(kcontext, me)->data,
516                                 tgtname.length, tgtname.data,
517                                 krb5_princ_realm(kcontext, me)->length,
518                                 krb5_princ_realm(kcontext, me)->data,
519                                 0)) {
520                 return 0;
521         }
522 #endif
523         my_creds.server = server;
524         if (krb5_timeofday(kcontext, &now))
525                 return 0;
526         my_creds.times.starttime = 0;
527         my_creds.times.endtime = now + lifetime;
528         my_creds.times.renew_till = now + renewal;
529
530         if (conf->krb_save_credentials) {
531                 if (krb5_cc_resolve(kcontext, ccname, &ccache))
532                         return 0;
533
534                 if (krb5_cc_initialize(kcontext, ccache, me))
535                         return 0;
536         }
537
538         ret = krb5_get_in_tkt_with_password(kcontext, options, 0, NULL, 0,
539                                 pass, ccache, &my_creds, 0);
540         if (ret) {
541                 return 0;
542         }
543
544         krb5_free_cred_contents(kcontext, &my_creds);
545
546         return 1;
547 }
548 #endif /* KRB5 */
549
550 #ifdef KRB4
551 int kerb4_password_validate(request_rec *r, const char *user, const char *pass)
552 {
553         kerb_auth_config *conf =
554                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
555                                         &kerb_auth_module);
556         int ret;
557         int lifetime = DEFAULT_TKT_LIFE;
558         char *c, *tfname;
559         char *username = NULL;
560         char *instance = NULL;
561         char *realm = NULL;
562
563         username = (char *)ap_pstrdup(r->pool, user);
564         if (!username) {
565                 return 0;
566         }
567
568         instance = strchr(username, '.');
569         if (instance) {
570                 *instance++ = '\0';
571         }
572         else {
573                 instance = "";
574         }
575
576         realm = strchr(username, '@');
577         if (realm) {
578                 *realm++ = '\0';
579         }
580         else {
581                 realm = "";
582         }
583
584         if (conf->krb_lifetime) {
585                 lifetime = atoi(conf->krb_lifetime);
586         }
587
588         if (conf->krb_force_instance) {
589                 instance = conf->krb_force_instance;
590         }
591
592         if (conf->krb_save_credentials) {
593                 tfname = (char *)malloc(sizeof(char) * MAX_STRING_LEN);
594                 sprintf(tfname, "%s/k5cc_ap_%s",
595                         conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
596                         MK_USER);
597
598                 if (!strcmp(instance, "")) {
599                         tfname = strcat(tfname, ".");
600                         tfname = strcat(tfname, instance);
601                 }
602
603                 if (!strcmp(realm, "")) {
604                         tfname = strcat(tfname, ".");
605                         tfname = strcat(tfname, realm);
606                 }
607
608                 for (c = tfname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
609                                 "/tmp") + 1; *c; c++) {
610                         if (*c == '/')
611                                 *c = '.';
612                 }
613
614                 krb_set_tkt_string(tfname);
615         }
616
617         if (!strcmp(realm, "")) {
618                 realm = (char *)malloc(sizeof(char) * (REALM_SZ + 1));
619                 ret = krb_get_lrealm(realm, 1);
620                 if (ret != KSUCCESS)
621                         return 0;
622         }
623
624         ret = krb_get_pw_in_tkt((char *)user, instance, realm, "krbtgt", realm,
625                                         lifetime, (char *)pass);
626         switch (ret) {
627                 case INTK_OK:
628                 case INTK_W_NOTALL:
629                         return 1;
630                         break;
631
632                 default:
633                         return 0;
634                         break;
635         }
636 }
637 #endif /* KRB4 */
638
639 static int
640 get_gss_creds(request_rec *r,
641               kerb_auth_config *conf,
642               gss_cred_id_t *server_creds)
643 {
644    int ret = 0;
645    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
646    OM_uint32 major_status, minor_status;
647    gss_name_t server_name = GSS_C_NO_NAME;
648
649    if (conf->service_name) {
650       input_token.value = conf->service_name;
651       input_token.length = strlen(conf->service_name) + 1;
652    }
653    else {
654       input_token.value = "khttp";
655       input_token.length = 6;
656    }
657    major_status = gss_import_name(&minor_status, &input_token,
658                                   (conf->service_name) ? 
659                                        GSS_C_NT_USER_NAME : GSS_C_NT_HOSTBASED_SERVICE,
660                                   &server_name);
661    if (GSS_ERROR(major_status)) {
662       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
663                     "%s", get_gss_error(r->pool, minor_status,
664                     "gss_import_name() failed"));
665       ret = SERVER_ERROR;
666       goto fail;
667    }
668    
669 #ifdef KRB5
670    if (conf->krb_5_keytab)
671       setenv("KRB5_KTNAME", conf->krb_5_keytab, 1);
672 #endif
673
674    major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
675                                    GSS_C_NO_OID_SET, GSS_C_ACCEPT,
676                                    server_creds, NULL, NULL);
677 #ifdef KRB5
678    if (conf->krb_5_keytab)
679       unsetenv("KRB5_KTNAME");
680 #endif
681    if (GSS_ERROR(major_status)) {
682       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
683                    "%s", get_gss_error(r->pool, minor_status,
684                                        "gss_acquire_cred() failed"));
685       ret = SERVER_ERROR;
686       goto fail;
687    }
688    
689    return 0;
690
691 fail:
692    /* XXX cleanup */
693
694    return ret;
695 }
696
697 static int
698 negotiate_authenticate_user(request_rec *r,
699                             kerb_auth_config *conf,
700                             const char *auth_line)
701 {
702   OM_uint32 major_status, minor_status, minor_status2;
703   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
704   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
705   const char *auth_param = NULL;
706   krb5_context krb_ctx = NULL;
707   int ret;
708   gss_name_t client_name = GSS_C_NO_NAME;
709   gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
710   char *p;
711
712   if (gss_connection == NULL) {
713      gss_connection = ap_pcalloc(r->connection->pool, sizeof(*gss_connection));
714      if (gss_connection == NULL) {
715         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
716                       "ap_pcalloc() failed");
717         ret = SERVER_ERROR;
718         goto end;
719      }
720      memset(gss_connection, 0, sizeof(*gss_connection));
721      ap_register_cleanup(r->connection->pool, gss_connection, cleanup_gss_connection, ap_null_cleanup);
722   }
723
724   if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
725      ret = get_gss_creds(r, conf, &gss_connection->server_creds);
726      if (ret)
727         goto end;
728   }
729
730   /* ap_getword() shifts parameter */
731   auth_param = ap_getword_white(r->pool, &auth_line);
732   if (auth_param == NULL) {
733      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
734                    "No Authorization parameter from client");
735      ret = HTTP_UNAUTHORIZED;
736      goto end;
737   }
738
739   input_token.length = ap_base64decode_len(auth_param);
740   input_token.value = ap_pcalloc(r->connection->pool, input_token.length);
741   if (input_token.value == NULL) {
742      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
743                    "Not enough memory");
744      ret = SERVER_ERROR;
745      goto end;
746   }
747   input_token.length = ap_base64decode(input_token.value, auth_param);
748
749   major_status = gss_accept_sec_context(&minor_status,
750                                         &gss_connection->context,
751                                         gss_connection->server_creds,
752                                         &input_token,
753                                         GSS_C_NO_CHANNEL_BINDINGS,
754                                         &client_name,
755                                         NULL,
756                                         &output_token,
757                                         NULL,
758                                         NULL,
759                                         &delegated_cred);
760   if (output_token.length) {
761      char *token = NULL;
762      size_t len;
763      
764      len = ap_base64encode_len(output_token.length);
765      token = ap_pcalloc(r->connection->pool, len + 1);
766      if (token == NULL) {
767         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
768                      "Not enough memory");
769         ret = SERVER_ERROR;
770         gss_release_buffer(&minor_status2, &output_token);
771         goto end;
772      }
773      ap_base64encode(token, output_token.value, output_token.length);
774      token[len] = '\0';
775      ap_table_set(r->err_headers_out, "WWW-Authenticate",
776                   ap_pstrcat(r->pool, "GSS-Negotiate ", token, NULL));
777      free(token);
778      gss_release_buffer(&minor_status2, &output_token);
779   }
780
781   if (GSS_ERROR(major_status)) {
782      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
783                    "%s", get_gss_error(r->pool, minor_status,
784                                        "gss_accept_sec_context() failed"));
785      ret = HTTP_UNAUTHORIZED;
786      goto end;
787   }
788
789   if (major_status & GSS_S_CONTINUE_NEEDED) {
790 #if 0
791      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
792                    "only one authentication iteration allowed"); 
793 #endif
794      ret = HTTP_UNAUTHORIZED;
795      goto end;
796   }
797
798   major_status = gss_export_name(&minor_status, client_name, &output_token);
799   gss_release_name(&minor_status, &client_name); 
800   if (GSS_ERROR(major_status)) {
801     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
802                   "%s", get_gss_error(r->pool, minor_status, 
803                                       "gss_export_name() failed"));
804     ret = SERVER_ERROR;
805     goto end;
806   }
807
808   r->connection->ap_auth_type = "Negotiate";
809   r->connection->user = ap_pstrdup(r->pool, output_token.value);
810 #if 0
811   /* If the user comes from a realm specified by configuration don't include
812       its realm name in the username so that the authorization routine could
813       work for both Password-based and Ticket-based authentication. It's
814       administrators responsibility to include only such realm that have
815       unified principal instances, i.e. if the same principal name occures in
816       multiple realms, it must be always assigned to a single user.
817   */    
818   p = strchr(r->connection->user, '@');
819   if (p != NULL) {
820      const char *realms = conf->gss_krb5_realms;
821
822      while (realms && *realms) {
823         if (strcmp(p+1, ap_getword_white(r->pool, &realms)) == 0) {
824            *p = '\0';
825            break;
826         }
827      }
828   }
829 #endif
830
831   gss_release_buffer(&minor_status, &output_token);
832
833 #if 0
834   /* This should be only done if afs token are requested or gss_save creds is 
835    * specified */
836   /* gss_export_cred() from the GGF GSS Extensions could be used */
837   if (delegated_cred != GSS_C_NO_CREDENTIAL &&
838       (conf->gss_save_creds || (conf->gss_krb5_cells && k_hasafs()))) { 
839      krb5_init_context(&krb_ctx);
840      do_afs_log(krb_ctx, r, delegated_cred->ccache, conf->gss_krb5_cells);
841      ret = store_krb5_creds(krb_ctx, r, conf, delegated_cred->ccache);
842      krb5_free_context(krb_ctx);
843      if (ret)
844         goto end;
845   }
846 #endif
847   ret = OK;
848
849 end:
850   if (delegated_cred)
851      gss_release_cred(&minor_status, &delegated_cred);
852
853   if (output_token.length) 
854      gss_release_buffer(&minor_status, &output_token);
855
856   if (client_name != GSS_C_NO_NAME)
857      gss_release_name(&minor_status, &client_name);
858
859   return ret;
860 }
861
862
863 /*************************************************************************** 
864  User Authentication
865  ***************************************************************************/
866 int kerb_authenticate_user(request_rec *r)
867 {
868         const char *name;               /* AuthName specified */
869         const char *type;               /* AuthType specified */
870         int KerberosV5 = 0;             /* Kerberos V5 check enabled */
871         int KerberosV4 = 0;             /* Kerberos V4 check enabled */
872         int KerberosV4first = 0;        /* Kerberos V4 check first */
873         const char *sent_pw;            /* Password sent by browser */
874         int res;                        /* Response holder */
875         int retcode;                    /* Return code holder */
876         const char *t;                  /* Decoded auth_line */
877         const char *authtype;           /* AuthType to send back to browser */
878         const char *auth_line = MK_TABLE_GET(r->headers_in,
879                                         (r->proxyreq == MK_PROXY)
880                                                 ? "Proxy-Authorization"
881                                                 : "Authorization");
882         kerb_auth_config *conf =
883                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
884                                         &kerb_auth_module);
885
886         type = ap_auth_type(r);
887
888         if (type != NULL) {
889 #ifdef KRB5
890                 if ((strncasecmp(type, "KerberosV5", 10) == 0) ||
891                     (strncasecmp(conf->krb_auth_type, "KerberosV5", 10) == 0)) {
892                         KerberosV5 = 1;
893                 }
894 #endif /* KRB5 */
895
896 #ifdef KRB4
897                 if ((strncasecmp(type, "KerberosV4", 10) == 0) ||
898                     (strncasecmp(conf->krb_auth_type, "KerberosV4", 10) == 0)) {
899                         KerberosV4 = 1;
900                 }
901 #endif /* KRB4 */
902
903 #if defined(KRB5) && defined(KRB4)
904                 if ((strncasecmp(type, "KerberosDualV5V4", 15) == 0) ||
905                     (strncasecmp(conf->krb_auth_type, "KerberosDualV5V4", 15) == 0)) {
906                         KerberosV5 = 1;
907                         KerberosV4 = 1;
908                 }
909
910                 if ((strncasecmp(type, "KerberosDualV4V5", 15) == 0) ||
911                     (strncasecmp(conf->krb_auth_type, "KerberosDualV4V5", 15) == 0)) {
912                         KerberosV5 = 1;
913                         KerberosV4 = 1;
914                         KerberosV4first = 1;
915                 }
916 #endif /* KRB5 && KRB4 */
917         }
918
919         if (!KerberosV4 && !KerberosV5) {
920                 if (conf->krb_authoritative) {
921                         return HTTP_UNAUTHORIZED;
922                 }
923                 else {
924                         return DECLINED;
925                 }
926         }
927
928         name = ap_auth_name(r);
929         if (!name) {
930                 return HTTP_INTERNAL_SERVER_ERROR;
931         }
932
933         if (!auth_line) {
934                 MK_TABLE_SET(r->err_headers_out, "WWW-Authenticate",
935                         (char *)ap_pstrcat(r->pool,
936                         "Basic realm=\"", name, "\"", NULL));
937                 return HTTP_UNAUTHORIZED;
938         }
939
940         type = ap_getword_white(r->pool, &auth_line);
941         t = ap_pbase64decode(r->pool, auth_line);
942         MK_USER = ap_getword_nulls(r->pool, &t, ':');
943         MK_AUTH_TYPE = "Kerberos";
944         sent_pw = ap_getword_white(r->pool, &t);
945
946         retcode = DECLINED;
947
948 #ifdef KRB5
949         if (KerberosV5 && !KerberosV4first && retcode != OK) {
950                 MK_AUTH_TYPE = "KerberosV5";
951                 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
952                         retcode = OK;
953                 }
954                 else {
955                         retcode = conf->krb_fail_status;
956                 }
957         }
958 #endif /* KRB5 */
959
960 #ifdef KRB4
961         if (KerberosV4 && retcode != OK) {
962                 MK_AUTH_TYPE = "KerberosV4";
963                 if (kerb4_password_validate(r, MK_USER, sent_pw)) {
964                         retcode = OK;
965                 }
966                 else {
967                         retcode = conf->krb_fail_status;
968                 }
969         }
970 #endif /* KRB4 */
971
972 #if defined(KRB5) && defined(KRB4)
973         if (KerberosV5 && KerberosV4first && retcode != OK) {
974                 MK_AUTH_TYPE = "KerberosV5";
975                 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
976                         retcode = OK;
977                 }
978                 else {
979                         retcode = conf->krb_fail_status;
980                 }
981         }
982 #endif /* KRB5 && KRB4 */
983
984         if (conf->krb_authoritative && retcode == DECLINED) {
985                 return HTTP_UNAUTHORIZED;
986         }
987         else {
988                 return retcode;
989         }
990 }
991
992
993
994
995 /*************************************************************************** 
996  Access Verification
997  ***************************************************************************/
998 int kerb_check_user_access(request_rec *r)
999 {
1000         register int x;
1001         const char *t, *w;
1002         const MK_ARRAY_HEADER *reqs_arr = ap_requires(r);
1003         require_line *reqs;
1004         kerb_auth_config *conf =
1005                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
1006                                                 &kerb_auth_module);
1007
1008         if (reqs_arr == NULL) {
1009                 return OK;
1010         }
1011         reqs = (require_line *)reqs_arr->elts;
1012
1013         for (x = 0; x < reqs_arr->nelts; x++) {
1014                 t = reqs[x].requirement;
1015                 w = ap_getword_white(r->pool, &t);
1016                 if (strcmp(w, "realm") == 0) {
1017                         while (t[0] != '\0') {
1018                                 w = ap_getword_conf(r->pool, &t);
1019                                 if (strcmp(MK_USER, w) == 0) {
1020                                         return OK;
1021                                 }
1022                         }
1023                 }
1024         }
1025
1026         return DECLINED;
1027 }
1028
1029
1030
1031
1032 /*************************************************************************** 
1033  Module Setup/Configuration
1034  ***************************************************************************/
1035 #ifdef APXS1
1036 module MODULE_VAR_EXPORT kerb_auth_module = {
1037         STANDARD_MODULE_STUFF,
1038         NULL,                           /*      module initializer            */
1039         kerb_dir_config,                /*      per-directory config creator  */
1040         NULL,                           /*      per-directory config merger   */
1041         NULL,                           /*      per-server    config creator  */
1042         NULL,                           /*      per-server    config merger   */
1043         kerb_auth_cmds,                 /*      command table                 */
1044         NULL,                           /* [ 9] content handlers              */
1045         NULL,                           /* [ 2] URI-to-filename translation   */
1046         kerb_authenticate_user,         /* [ 5] check/validate user_id        */
1047         kerb_check_user_access,         /* [ 6] check user_id is valid *here* */
1048         NULL,                           /* [ 4] check access by host address  */
1049         NULL,                           /* [ 7] MIME type checker/setter      */
1050         NULL,                           /* [ 8] fixups                        */
1051         NULL,                           /* [10] logger                        */
1052         NULL,                           /* [ 3] header parser                 */
1053         NULL,                           /*      process initialization        */
1054         NULL,                           /*      process exit/cleanup          */
1055         NULL                            /* [ 1] post read_request handling    */
1056 #ifdef EAPI
1057         ,                               /*            EAPI Additions          */
1058         NULL,                           /* EAPI add module                    */
1059         NULL,                           /* EAPI remove module                 */
1060         NULL,                           /* EAPI rewrite command               */
1061         NULL                            /* EAPI new connection                */
1062 #endif /* EAPI */
1063 };
1064 #else
1065 #ifdef APXS2
1066 void kerb_register_hooks(apr_pool_t *p)
1067 {
1068         ap_hook_check_user_id(kerb_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
1069         ap_hook_access_checker(kerb_check_user_access, NULL, NULL, APR_HOOK_MIDDLE);
1070 }
1071
1072 module AP_MODULE_DECLARE_DATA kerb_auth_module =
1073 {
1074         STANDARD20_MODULE_STUFF,
1075         kerb_dir_config,                /* create per-dir    conf structures  */
1076         NULL,                           /* merge  per-dir    conf structures  */
1077         NULL,                           /* create per-server conf structures  */
1078         NULL,                           /* merge  per-server conf structures  */
1079         kerb_auth_cmds,                 /* table of configuration directives  */
1080         kerb_register_hooks             /* register hooks                     */
1081 };
1082 #endif /* APXS2 */
1083 #endif /* APXS1 */