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