b6169d88310f0dabf4e07d9009aa0468407137ea
[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 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 #ifdef HEIMDAL
716         code = krb5_cc_gen_new(kcontext, &krb5_mcc_ops, &ccache);
717 #else
718         code = krb5_mcc_generate_new(kcontext, &ccache);
719 #endif
720         if (code) {
721            snprintf(errstr, sizeof(errstr), "Cannot generate new ccache: %.100s",
722                     krb5_get_err_text(kcontext, code));
723            ap_log_reason (errstr, r->uri, r);
724            ret = SERVER_ERROR;
725            goto end;
726         }
727
728         realms = conf->krb_auth_realms;
729         do {
730            code = 0;
731            if (realms) {
732               code = krb5_set_default_realm(kcontext, 
733                                             ap_getword_white(r->pool, &realms));
734               if (code)
735                  continue;
736            }
737
738            code = krb5_parse_name(kcontext, r->connection->user, &client);
739            if (code)
740               continue;
741
742            code = krb5_verify_user(kcontext, client, ccache, pass,
743                                    1, "khttp");
744            krb5_free_principal(kcontext, client);
745            if (code == 0)
746               break;
747
748            /* ap_getword_white() used above shifts the parameter, so it's not
749               needed to touch the realms variable */
750         } while (realms && *realms);
751
752         memset((char *)pass, 0, strlen(pass));
753
754         if (code) {
755            snprintf(errstr, sizeof(errstr), "Verifying krb5 password failed: %s",
756                     krb5_get_err_text(kcontext, code));
757            ap_log_reason (errstr, r->uri, r);
758            ret = HTTP_UNAUTHORIZED;
759            goto end;
760         }
761
762         if (conf->krb_save_credentials) {
763                 ret = store_krb5_creds(kcontext, r, conf, ccache);
764                 if (ret)
765                         goto end;
766         }
767
768         ret = 1; /* XXX should be OK ? */
769
770 end:
771         if (ccache)
772            krb5_cc_destroy(kcontext, ccache);
773         krb5_free_context(kcontext);
774
775         return (ret != 1) ? 0 : 1; /* XXX */
776 }
777 #endif /* KRB5 */
778
779 #ifdef KRB4
780 int kerb4_password_validate(request_rec *r, const char *user, const char *pass)
781 {
782         kerb_auth_config *conf =
783                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
784                                         &kerb_auth_module);
785         int ret;
786         int lifetime = DEFAULT_TKT_LIFE;
787         char *c, *tfname;
788         char *username = NULL;
789         char *instance = NULL;
790         char *realm = NULL;
791
792         username = (char *)ap_pstrdup(r->pool, user);
793         if (!username) {
794                 return 0;
795         }
796
797         instance = strchr(username, '.');
798         if (instance) {
799                 *instance++ = '\0';
800         }
801         else {
802                 instance = "";
803         }
804
805         realm = strchr(username, '@');
806         if (realm) {
807                 *realm++ = '\0';
808         }
809         else {
810                 realm = "";
811         }
812
813         if (conf->krb_lifetime) {
814                 lifetime = atoi(conf->krb_lifetime);
815         }
816
817         if (conf->krb_force_instance) {
818                 instance = conf->krb_force_instance;
819         }
820
821         if (conf->krb_save_credentials) {
822                 tfname = (char *)malloc(sizeof(char) * MAX_STRING_LEN);
823                 sprintf(tfname, "%s/k5cc_ap_%s",
824                         conf->krb_tmp_dir ? conf->krb_tmp_dir : "/tmp",
825                         MK_USER);
826
827                 if (!strcmp(instance, "")) {
828                         tfname = strcat(tfname, ".");
829                         tfname = strcat(tfname, instance);
830                 }
831
832                 if (!strcmp(realm, "")) {
833                         tfname = strcat(tfname, ".");
834                         tfname = strcat(tfname, realm);
835                 }
836
837                 for (c = tfname + strlen(conf->krb_tmp_dir ? conf->krb_tmp_dir :
838                                 "/tmp") + 1; *c; c++) {
839                         if (*c == '/')
840                                 *c = '.';
841                 }
842
843                 krb_set_tkt_string(tfname);
844         }
845
846         if (!strcmp(realm, "")) {
847                 realm = (char *)malloc(sizeof(char) * (REALM_SZ + 1));
848                 ret = krb_get_lrealm(realm, 1);
849                 if (ret != KSUCCESS)
850                         return 0;
851         }
852
853         ret = krb_get_pw_in_tkt((char *)user, instance, realm, "krbtgt", realm,
854                                         lifetime, (char *)pass);
855         switch (ret) {
856                 case INTK_OK:
857                 case INTK_W_NOTALL:
858                         return 1;
859                         break;
860
861                 default:
862                         return 0;
863                         break;
864         }
865 }
866 #endif /* KRB4 */
867
868
869
870
871 /*************************************************************************** 
872  GSSAPI Validation
873  ***************************************************************************/
874 #ifdef GSSAPI
875 static const char *
876 get_gss_error(pool *p, OM_uint32 error_status, char *prefix)
877 {
878    OM_uint32 maj_stat, min_stat;
879    OM_uint32 msg_ctx = 0;
880    gss_buffer_desc status_string;
881    char buf[1024];
882    size_t len;
883
884    snprintf(buf, sizeof(buf), "%s: ", prefix);
885    len = strlen(buf);
886    do {
887       maj_stat = gss_display_status (&min_stat,
888                                      error_status,
889                                      GSS_C_MECH_CODE,
890                                      GSS_C_NO_OID,
891                                      &msg_ctx,
892                                      &status_string);
893       if (sizeof(buf) > len + status_string.length + 1) {
894          sprintf(buf+len, "%s:", (char*) status_string.value);
895          len += status_string.length;
896       }
897       gss_release_buffer(&min_stat, &status_string);
898    } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
899
900    return (ap_pstrdup(p, buf));
901 }
902
903 static int
904 get_gss_creds(request_rec *r,
905               kerb_auth_config *conf,
906               gss_cred_id_t *server_creds)
907 {
908    int ret = 0;
909    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
910    OM_uint32 major_status, minor_status;
911    gss_name_t server_name = GSS_C_NO_NAME;
912
913    if (conf->service_name) {
914       input_token.value = conf->service_name;
915       input_token.length = strlen(conf->service_name) + 1;
916    }
917    else {
918       input_token.value = "khttp";
919       input_token.length = 6;
920    }
921    major_status = gss_import_name(&minor_status, &input_token,
922                                   (conf->service_name) ? 
923                                        GSS_C_NT_USER_NAME : GSS_C_NT_HOSTBASED_SERVICE,
924                                   &server_name);
925    if (GSS_ERROR(major_status)) {
926       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
927                     "%s", get_gss_error(r->pool, minor_status,
928                     "gss_import_name() failed"));
929       ret = SERVER_ERROR;
930       goto fail;
931    }
932    
933 #ifdef KRB5
934    if (conf->krb_5_keytab)
935       setenv("KRB5_KTNAME", conf->krb_5_keytab, 1);
936 #endif
937
938    major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
939                                    GSS_C_NO_OID_SET, GSS_C_ACCEPT,
940                                    server_creds, NULL, NULL);
941 #ifdef KRB5
942    if (conf->krb_5_keytab)
943       unsetenv("KRB5_KTNAME");
944 #endif
945    if (GSS_ERROR(major_status)) {
946       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
947                    "%s", get_gss_error(r->pool, minor_status,
948                                        "gss_acquire_cred() failed"));
949       ret = SERVER_ERROR;
950       goto fail;
951    }
952    
953    return 0;
954
955 fail:
956    /* XXX cleanup */
957
958    return ret;
959 }
960
961 static int
962 negotiate_authenticate_user(request_rec *r,
963                             kerb_auth_config *conf,
964                             const char *auth_line)
965 {
966   OM_uint32 major_status, minor_status, minor_status2;
967   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
968   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
969   const char *auth_param = NULL;
970   int ret;
971   gss_name_t client_name = GSS_C_NO_NAME;
972   gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
973
974   if (gss_connection == NULL) {
975      gss_connection = ap_pcalloc(r->connection->pool, sizeof(*gss_connection));
976      if (gss_connection == NULL) {
977         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
978                       "ap_pcalloc() failed (not enough memory)");
979         ret = SERVER_ERROR;
980         goto end;
981      }
982      memset(gss_connection, 0, sizeof(*gss_connection));
983      ap_register_cleanup(r->connection->pool, gss_connection, cleanup_gss_connection, ap_null_cleanup);
984   }
985
986   if (gss_connection->server_creds == GSS_C_NO_CREDENTIAL) {
987      ret = get_gss_creds(r, conf, &gss_connection->server_creds);
988      if (ret)
989         goto end;
990   }
991
992   /* ap_getword() shifts parameter */
993   auth_param = ap_getword_white(r->pool, &auth_line);
994   if (auth_param == NULL) {
995      ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
996                    "No Authorization parameter in request from client");
997      ret = HTTP_UNAUTHORIZED;
998      goto end;
999   }
1000
1001   input_token.length = ap_base64decode_len(auth_param);
1002   input_token.value = ap_pcalloc(r->connection->pool, input_token.length);
1003   if (input_token.value == NULL) {
1004      ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1005                    "ap_pcalloc() failed (not enough memory)");
1006      ret = SERVER_ERROR;
1007      goto end;
1008   }
1009   input_token.length = ap_base64decode(input_token.value, auth_param);
1010
1011   major_status = gss_accept_sec_context(&minor_status,
1012                                         &gss_connection->context,
1013                                         gss_connection->server_creds,
1014                                         &input_token,
1015                                         GSS_C_NO_CHANNEL_BINDINGS,
1016                                         &client_name,
1017                                         NULL,
1018                                         &output_token,
1019                                         NULL,
1020                                         NULL,
1021                                         &delegated_cred);
1022   if (output_token.length) {
1023      char *token = NULL;
1024      size_t len;
1025      
1026      len = ap_base64encode_len(output_token.length);
1027      token = ap_pcalloc(r->connection->pool, len + 1);
1028      if (token == NULL) {
1029         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1030                      "ap_pcalloc() failed (not enough memory)");
1031         ret = SERVER_ERROR;
1032         gss_release_buffer(&minor_status2, &output_token);
1033         goto end;
1034      }
1035      ap_base64encode(token, output_token.value, output_token.length);
1036      token[len] = '\0';
1037      ap_table_set(r->err_headers_out, "WWW-Authenticate",
1038                   ap_pstrcat(r->pool, "GSS-Negotiate ", token, NULL));
1039      gss_release_buffer(&minor_status2, &output_token);
1040   }
1041
1042   if (GSS_ERROR(major_status)) {
1043      ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1044                    "%s", get_gss_error(r->pool, minor_status,
1045                                        "gss_accept_sec_context() failed"));
1046      ret = HTTP_UNAUTHORIZED;
1047      goto end;
1048   }
1049
1050   if (major_status & GSS_S_CONTINUE_NEEDED) {
1051      /* Some GSSAPI mechanism (eg GSI from Globus) may require multiple 
1052       * iterations to establish authentication */
1053      ret = HTTP_UNAUTHORIZED;
1054      goto end;
1055   }
1056
1057   major_status = gss_export_name(&minor_status, client_name, &output_token);
1058   gss_release_name(&minor_status, &client_name); 
1059   if (GSS_ERROR(major_status)) {
1060     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1061                   "%s", get_gss_error(r->pool, minor_status, 
1062                                       "gss_export_name() failed"));
1063     ret = SERVER_ERROR;
1064     goto end;
1065   }
1066
1067   r->connection->ap_auth_type = "Negotiate";
1068   r->connection->user = ap_pstrdup(r->pool, output_token.value);
1069 #if 0
1070   /* If the user comes from a realm specified by configuration don't include
1071       its realm name in the username so that the authorization routine could
1072       work for both Password-based and Ticket-based authentication. It's
1073       administrators responsibility to include only such realm that have
1074       unified principal instances, i.e. if the same principal name occures in
1075       multiple realms, it must be always assigned to a single user.
1076   */    
1077   p = strchr(r->connection->user, '@');
1078   if (p != NULL) {
1079      const char *realms = conf->gss_krb5_realms;
1080
1081      while (realms && *realms) {
1082         if (strcmp(p+1, ap_getword_white(r->pool, &realms)) == 0) {
1083            *p = '\0';
1084            break;
1085         }
1086      }
1087   }
1088 #endif
1089
1090   gss_release_buffer(&minor_status, &output_token);
1091
1092 #if 0
1093   /* This should be only done if afs token are requested or gss_save creds is 
1094    * specified */
1095   /* gss_export_cred() from the GGF GSS Extensions could be used */
1096   if (delegated_cred != GSS_C_NO_CREDENTIAL &&
1097       (conf->gss_save_creds || (conf->gss_krb5_cells && k_hasafs()))) { 
1098      krb5_init_context(&krb_ctx);
1099      do_afs_log(krb_ctx, r, delegated_cred->ccache, conf->gss_krb5_cells);
1100      ret = store_krb5_creds(krb_ctx, r, conf, delegated_cred->ccache);
1101      krb5_free_context(krb_ctx);
1102      if (ret)
1103         goto end;
1104   }
1105 #endif
1106   ret = OK;
1107
1108 end:
1109   if (delegated_cred)
1110      gss_release_cred(&minor_status, &delegated_cred);
1111
1112   if (output_token.length) 
1113      gss_release_buffer(&minor_status, &output_token);
1114
1115   if (client_name != GSS_C_NO_NAME)
1116      gss_release_name(&minor_status, &client_name);
1117
1118   return ret;
1119 }
1120 #endif /* GSSAPI */
1121
1122
1123
1124
1125 /*************************************************************************** 
1126  User Authentication
1127  ***************************************************************************/
1128 int kerb_authenticate_user(request_rec *r)
1129 {
1130         const char *name;               /* AuthName specified */
1131         const char *type;               /* AuthType specified */
1132         const char *sent_pw;            /* Password sent by browser */
1133         int res;                        /* Response holder */
1134         int retcode;                    /* Return code holder */
1135         const char *t;                  /* Decoded auth_line */
1136         const char *authtype;           /* AuthType to send back to browser */
1137         const char *auth_line = MK_TABLE_GET(r->headers_in,
1138                                         (r->proxyreq == MK_PROXY)
1139                                                 ? "Proxy-Authorization"
1140                                                 : "Authorization");
1141         kerb_auth_config *conf =
1142                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
1143                                         &kerb_auth_module);
1144
1145         type = ap_auth_type(r);
1146
1147         if (!conf->krb_auth_enable &&
1148             (type == NULL || (strncasecmp(type, "Kerberos", 8) != 0))) {
1149                 if (conf->krb_authoritative) {
1150                         return HTTP_UNAUTHORIZED;
1151                 }
1152                 else {
1153                         return DECLINED;
1154                 }
1155         }
1156
1157         name = ap_auth_name(r);
1158         if (!name) {
1159                 return HTTP_INTERNAL_SERVER_ERROR;
1160         }
1161
1162         if (!auth_line) {
1163                 MK_TABLE_SET(r->err_headers_out, "WWW-Authenticate",
1164                         (char *)ap_pstrcat(r->pool,
1165                         "Basic realm=\"", name, "\"", NULL));
1166                 return HTTP_UNAUTHORIZED;
1167         }
1168
1169         type = ap_getword_white(r->pool, &auth_line);
1170         t = ap_pbase64decode(r->pool, auth_line);
1171         MK_USER = ap_getword_nulls(r->pool, &t, ':');
1172         MK_AUTH_TYPE = "Kerberos";
1173         sent_pw = ap_getword_white(r->pool, &t);
1174
1175         retcode = DECLINED;
1176
1177 #ifdef GSSAPI
1178         if (conf->krb_method_gssapi && retcode != OK) {
1179                 MK_AUTH_TYPE = "Negotiate";
1180                 if (negotiate_authenticate_user(r, conf, auth_line))
1181                         retcode = OK;
1182                 else
1183                         retcode = conf->krb_fail_status;
1184         }
1185 #endif /* GSSAPI */
1186
1187 #ifdef KRB5
1188         if (conf->krb_method_k5pass && retcode != OK) {
1189                 MK_AUTH_TYPE = "KerberosV5";
1190                 if (kerb5_password_validate(r, MK_USER, sent_pw)) {
1191                         retcode = OK;
1192                 }
1193                 else {
1194                         retcode = conf->krb_fail_status;
1195                         /* XXX should SERVER_ERROR be overriden too? */
1196                 }
1197         }
1198 #endif /* KRB5 */
1199
1200 #ifdef KRB4
1201         if (conf->krb_method_k4pass && retcode != OK) {
1202                 MK_AUTH_TYPE = "KerberosV4";
1203                 if (kerb4_password_validate(r, MK_USER, sent_pw)) {
1204                         retcode = OK;
1205                 }
1206                 else {
1207                         retcode = conf->krb_fail_status;
1208                 }
1209         }
1210 #endif /* KRB4 */
1211
1212         if (conf->krb_authoritative && retcode == DECLINED) {
1213                 return HTTP_UNAUTHORIZED;
1214         }
1215         else {
1216                 return retcode;
1217         }
1218 }
1219
1220
1221
1222
1223 /*************************************************************************** 
1224  Access Verification
1225  ***************************************************************************/
1226 int kerb_check_user_access(request_rec *r)
1227 {
1228         register int x;
1229         const char *t, *w;
1230         const MK_ARRAY_HEADER *reqs_arr = ap_requires(r);
1231         require_line *reqs;
1232         kerb_auth_config *conf =
1233                 (kerb_auth_config *)ap_get_module_config(r->per_dir_config,
1234                                                 &kerb_auth_module);
1235
1236         if (reqs_arr == NULL) {
1237                 return OK;
1238         }
1239         reqs = (require_line *)reqs_arr->elts;
1240
1241         for (x = 0; x < reqs_arr->nelts; x++) {
1242                 t = reqs[x].requirement;
1243                 w = ap_getword_white(r->pool, &t);
1244                 if (strcmp(w, "realm") == 0) {
1245                         while (t[0] != '\0') {
1246                                 w = ap_getword_conf(r->pool, &t);
1247                                 if (strcmp(MK_USER, w) == 0) {
1248                                         return OK;
1249                                 }
1250                         }
1251                 }
1252         }
1253
1254         return DECLINED;
1255 }
1256
1257
1258
1259
1260 /*************************************************************************** 
1261  Module Setup/Configuration
1262  ***************************************************************************/
1263 #ifdef APXS1
1264 module MODULE_VAR_EXPORT kerb_auth_module = {
1265         STANDARD_MODULE_STUFF,
1266         NULL,                           /*      module initializer            */
1267         kerb_dir_config,                /*      per-directory config creator  */
1268         NULL,                           /*      per-directory config merger   */
1269         NULL,                           /*      per-server    config creator  */
1270         NULL,                           /*      per-server    config merger   */
1271         kerb_auth_cmds,                 /*      command table                 */
1272         NULL,                           /* [ 9] content handlers              */
1273         NULL,                           /* [ 2] URI-to-filename translation   */
1274         kerb_authenticate_user,         /* [ 5] check/validate user_id        */
1275         kerb_check_user_access,         /* [ 6] check user_id is valid *here* */
1276         NULL,                           /* [ 4] check access by host address  */
1277         NULL,                           /* [ 7] MIME type checker/setter      */
1278         NULL,                           /* [ 8] fixups                        */
1279         NULL,                           /* [10] logger                        */
1280         NULL,                           /* [ 3] header parser                 */
1281         NULL,                           /*      process initialization        */
1282         NULL,                           /*      process exit/cleanup          */
1283         NULL                            /* [ 1] post read_request handling    */
1284 #ifdef EAPI
1285         ,                               /*            EAPI Additions          */
1286         NULL,                           /* EAPI add module                    */
1287         NULL,                           /* EAPI remove module                 */
1288         NULL,                           /* EAPI rewrite command               */
1289         NULL                            /* EAPI new connection                */
1290 #endif /* EAPI */
1291 };
1292 #else
1293 #ifdef APXS2
1294 void kerb_register_hooks(apr_pool_t *p)
1295 {
1296         ap_hook_check_user_id(kerb_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
1297         ap_hook_access_checker(kerb_check_user_access, NULL, NULL, APR_HOOK_MIDDLE);
1298 }
1299
1300 module AP_MODULE_DECLARE_DATA kerb_auth_module =
1301 {
1302         STANDARD20_MODULE_STUFF,
1303         kerb_dir_config,                /* create per-dir    conf structures  */
1304         NULL,                           /* merge  per-dir    conf structures  */
1305         NULL,                           /* create per-server conf structures  */
1306         NULL,                           /* merge  per-server conf structures  */
1307         kerb_auth_cmds,                 /* table of configuration directives  */
1308         kerb_register_hooks             /* register hooks                     */
1309 };
1310 #endif /* APXS2 */
1311 #endif /* APXS1 */