Skip spnego filtering since we already filter it
[mod_auth_gssapi.git] / src / mod_auth_gssapi.c
1 /*
2    MOD AUTH GSSAPI
3
4    Copyright (C) 2014 Simo Sorce <simo@redhat.com>
5
6    Permission is hereby granted, free of charge, to any person obtaining a
7    copy of this software and associated documentation files (the "Software"),
8    to deal in the Software without restriction, including without limitation
9    the rights to use, copy, modify, merge, publish, distribute, sublicense,
10    and/or sell copies of the Software, and to permit persons to whom the
11    Software is furnished to do so, subject to the following conditions:
12
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22    DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "mod_auth_gssapi.h"
26
27 const gss_OID_desc gss_mech_spnego = {
28     6, "\x2b\x06\x01\x05\x05\x02"
29 };
30
31 const gss_OID_desc gss_mech_ntlmssp = {
32     GSS_NTLMSSP_OID_LENGTH, GSS_NTLMSSP_OID_STRING
33 };
34
35 const gss_OID_set_desc gss_mech_set_ntlmssp = {
36     1, discard_const(&gss_mech_ntlmssp)
37 };
38
39 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
40
41 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
42
43 APLOG_USE_MODULE(auth_gssapi);
44
45 static char *mag_status(request_rec *req, int type, uint32_t err)
46 {
47     uint32_t maj_ret, min_ret;
48     gss_buffer_desc text;
49     uint32_t msg_ctx;
50     char *msg_ret;
51     int len;
52
53     msg_ret = NULL;
54     msg_ctx = 0;
55     do {
56         maj_ret = gss_display_status(&min_ret, err, type,
57                                      GSS_C_NO_OID, &msg_ctx, &text);
58         if (maj_ret != GSS_S_COMPLETE) {
59             return msg_ret;
60         }
61
62         len = text.length;
63         if (msg_ret) {
64             msg_ret = apr_psprintf(req->pool, "%s, %*s",
65                                    msg_ret, len, (char *)text.value);
66         } else {
67             msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
68         }
69         gss_release_buffer(&min_ret, &text);
70     } while (msg_ctx != 0);
71
72     return msg_ret;
73 }
74
75 static char *mag_error(request_rec *req, const char *msg,
76                        uint32_t maj, uint32_t min)
77 {
78     char *msg_maj;
79     char *msg_min;
80
81     msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
82     msg_min = mag_status(req, GSS_C_MECH_CODE, min);
83     return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
84 }
85
86 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
87
88 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
89                            apr_pool_t *temp, server_rec *s)
90 {
91     /* FIXME: create mutex to deal with connections and contexts ? */
92     mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
93     mag_post_config_session();
94     ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
95
96     return OK;
97 }
98
99 static int mag_pre_connection(conn_rec *c, void *csd)
100 {
101     struct mag_conn *mc;
102
103     mc = mag_new_conn_ctx(c->pool);
104     ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
105     return OK;
106 }
107
108 static apr_status_t mag_conn_destroy(void *ptr)
109 {
110     struct mag_conn *mc = (struct mag_conn *)ptr;
111     uint32_t min;
112
113     if (mc->ctx) {
114         (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
115     }
116     return APR_SUCCESS;
117 }
118
119 struct mag_conn *mag_new_conn_ctx(apr_pool_t *pool)
120 {
121     struct mag_conn *mc;
122
123     mc = apr_pcalloc(pool, sizeof(struct mag_conn));
124     apr_pool_create(&mc->pool, pool);
125     /* register the context in the memory pool, so it can be freed
126      * when the connection/request is terminated */
127     apr_pool_cleanup_register(mc->pool, (void *)mc,
128                               mag_conn_destroy, apr_pool_cleanup_null);
129
130     return mc;
131 }
132
133 static void mag_conn_clear(struct mag_conn *mc)
134 {
135     (void)mag_conn_destroy(mc);
136     apr_pool_t *temp;
137
138     apr_pool_clear(mc->pool);
139     temp = mc->pool;
140     memset(mc, 0, sizeof(struct mag_conn));
141     mc->pool = temp;
142 }
143
144 static bool mag_conn_is_https(conn_rec *c)
145 {
146     if (mag_is_https) {
147         if (mag_is_https(c)) return true;
148     }
149
150     return false;
151 }
152
153 static bool mag_acquire_creds(request_rec *req,
154                               struct mag_config *cfg,
155                               gss_OID_set desired_mechs,
156                               gss_cred_usage_t cred_usage,
157                               gss_cred_id_t *creds,
158                               gss_OID_set *actual_mechs)
159 {
160     uint32_t maj, min;
161 #ifdef HAVE_CRED_STORE
162     gss_const_key_value_set_t store = cfg->cred_store;
163
164     maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
165                                 desired_mechs, cred_usage, store, creds,
166                                 actual_mechs, NULL);
167 #else
168     maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
169                            desired_mechs, cred_usage, creds,
170                            actual_mechs, NULL);
171 #endif
172
173     if (GSS_ERROR(maj)) {
174         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
175                       mag_error(req, "gss_acquire_cred[_from]() "
176                                 "failed to get server creds",
177                                 maj, min));
178         return false;
179     }
180
181     return true;
182 }
183
184 #ifdef HAVE_CRED_STORE
185 static char *escape(apr_pool_t *pool, const char *name,
186                     char find, const char *replace)
187 {
188     char *escaped = NULL;
189     char *namecopy;
190     char *n;
191     char *p;
192
193     namecopy = apr_pstrdup(pool, name);
194
195     p = strchr(namecopy, find);
196     if (!p) return namecopy;
197
198     /* first segment */
199     n = namecopy;
200     while (p) {
201         /* terminate previous segment */
202         *p = '\0';
203         if (escaped) {
204             escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
205         } else {
206             escaped = apr_pstrcat(pool, n, replace, NULL);
207         }
208         /* move to next segment */
209         n = p + 1;
210         p = strchr(n, find);
211     }
212     /* append last segment if any */
213     if (*n) {
214         escaped = apr_pstrcat(pool, escaped, n, NULL);
215     }
216
217     return escaped;
218 }
219
220 static char *mag_gss_name_to_ccache_name(request_rec *req,
221                                          char *dir, const char *gss_name)
222 {
223     char *escaped;
224
225     /* We need to escape away '/', we can't have path separators in
226      * a ccache file name */
227     /* first double escape the esacping char (~) if any */
228     escaped = escape(req->pool, gss_name, '~', "~~");
229     /* then escape away the separator (/) if any */
230     escaped = escape(req->pool, escaped, '/', "~");
231
232     return apr_psprintf(req->pool, "%s/%s", dir, escaped);
233 }
234
235 static void mag_set_KRB5CCANME(request_rec *req, char *ccname)
236 {
237     apr_status_t status;
238     apr_finfo_t finfo;
239     char *value;
240
241     status = apr_stat(&finfo, ccname, APR_FINFO_MIN, req->pool);
242     if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
243         /* set the file cache anyway, but warn */
244         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
245                       "KRB5CCNAME file (%s) lookup failed!", ccname);
246     }
247
248     value = apr_psprintf(req->pool, "FILE:%s", ccname);
249     apr_table_set(req->subprocess_env, "KRB5CCNAME", value);
250 }
251
252 static void mag_store_deleg_creds(request_rec *req,
253                                   char *dir, char *clientname,
254                                   gss_cred_id_t delegated_cred,
255                                   char **ccachefile)
256 {
257     gss_key_value_element_desc element;
258     gss_key_value_set_desc store;
259     char *ccname;
260     uint32_t maj, min;
261     element.key = "ccache";
262     store.elements = &element;
263     store.count = 1;
264
265     ccname = mag_gss_name_to_ccache_name(req, dir, clientname);
266     element.value = apr_psprintf(req->pool, "FILE:%s", ccname);
267
268     maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
269                               GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
270     if (GSS_ERROR(maj)) {
271         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
272                       mag_error(req, "failed to store delegated creds",
273                                 maj, min));
274     }
275
276     *ccachefile = ccname;
277 }
278 #endif
279
280 static bool parse_auth_header(apr_pool_t *pool, const char **auth_header,
281                               gss_buffer_t value)
282 {
283     char *auth_header_value;
284
285     auth_header_value = ap_getword_white(pool, auth_header);
286     if (!auth_header_value) return false;
287     value->length = apr_base64_decode_len(auth_header_value) + 1;
288     value->value = apr_pcalloc(pool, value->length);
289     if (!value->value) return false;
290     value->length = apr_base64_decode(value->value, auth_header_value);
291
292     return true;
293 }
294
295 static bool is_mech_allowed(struct mag_config *cfg, gss_const_OID mech)
296 {
297     if (cfg->allowed_mechs == GSS_C_NO_OID_SET) return true;
298
299     for (int i = 0; i < cfg->allowed_mechs->count; i++) {
300         if (gss_oid_equal(&cfg->allowed_mechs->elements[i], mech)) {
301             return true;
302         }
303     }
304     return false;
305 }
306
307 #define AUTH_TYPE_NEGOTIATE 0
308 #define AUTH_TYPE_BASIC 1
309 #define AUTH_TYPE_RAW_NTLM 2
310 const char *auth_types[] = {
311     "Negotiate",
312     "Basic",
313     "NTLM",
314     NULL
315 };
316
317 static void mag_set_req_data(request_rec *req,
318                              struct mag_config *cfg,
319                              struct mag_conn *mc)
320 {
321     apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
322     apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
323                   apr_psprintf(req->pool,
324                                "%ld", (long)mc->expiration));
325     req->ap_auth_type = apr_pstrdup(req->pool,
326                                     auth_types[mc->auth_type]);
327     req->user = apr_pstrdup(req->pool, mc->user_name);
328     if (cfg->deleg_ccache_dir && mc->delegated) {
329         char *ccname;
330         ccname = mag_gss_name_to_ccache_name(req,
331                                              cfg->deleg_ccache_dir,
332                                              mc->gss_name);
333         if (ccname) {
334             mag_set_KRB5CCANME(req, ccname);
335         }
336     }
337 }
338
339 gss_OID_set mag_filter_unwanted_mechs(gss_OID_set src)
340 {
341     gss_const_OID unwanted_mechs[] = {
342         &gss_mech_spnego,
343         gss_mech_krb5_old,
344         gss_mech_krb5_wrong,
345         gss_mech_iakerb,
346         GSS_C_NO_OID
347     };
348     gss_OID_set dst;
349     uint32_t maj, min;
350     int present = 0;
351
352     for (int i = 0; unwanted_mechs[i] != GSS_C_NO_OID; i++) {
353         maj = gss_test_oid_set_member(&min,
354                                       discard_const(unwanted_mechs[i]),
355                                       src, &present);
356         if (present) break;
357     }
358     if (present) {
359         maj = gss_create_empty_oid_set(&min, &dst);
360         if (maj != GSS_S_COMPLETE) {
361             return GSS_C_NO_OID_SET;
362         }
363         for (int i = 0; i < src->count; i++) {
364             present = 0;
365             for (int j = 0; unwanted_mechs[j] != GSS_C_NO_OID; j++) {
366                 if (gss_oid_equal(&src->elements[i], unwanted_mechs[j])) {
367                     present = 1;
368                     break;
369                 }
370             }
371             if (present) continue;
372             maj = gss_add_oid_set_member(&min, &src->elements[i], &dst);
373             if (maj != GSS_S_COMPLETE) {
374                 gss_release_oid_set(&min, &dst);
375                 return GSS_C_NO_OID_SET;
376             }
377         }
378         return dst;
379     }
380     return src;
381 }
382
383 static bool mag_auth_basic(request_rec *req,
384                            struct mag_config *cfg,
385                            gss_buffer_desc ba_user,
386                            gss_buffer_desc ba_pwd,
387                            gss_cred_usage_t cred_usage,
388                            gss_name_t *client,
389                            gss_OID *mech_type,
390                            gss_cred_id_t *delegated_cred,
391                            uint32_t *vtime)
392 {
393 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
394     const char *user_ccache = NULL;
395     const char *orig_ccache = NULL;
396     long long unsigned int rndname;
397     apr_status_t rs;
398 #endif
399     gss_name_t user = GSS_C_NO_NAME;
400     gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
401     gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
402     gss_name_t server = GSS_C_NO_NAME;
403     gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
404     gss_ctx_id_t server_ctx = GSS_C_NO_CONTEXT;
405     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
406     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
407     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
408     gss_OID_set indicated_mechs = GSS_C_NO_OID_SET;
409     gss_OID_set allowed_mechs;
410     gss_OID_set filtered_mechs;
411     gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
412     uint32_t init_flags = 0;
413     uint32_t maj, min;
414     int present = 0;
415     bool ret = false;
416
417     maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &user);
418     if (GSS_ERROR(maj)) {
419         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
420                       "In Basic Auth, %s",
421                       mag_error(req, "gss_import_name() failed",
422                                 maj, min));
423         goto done;
424     }
425
426     if (cfg->basic_mechs) {
427         allowed_mechs = cfg->basic_mechs;
428     } else if (cfg->allowed_mechs) {
429         allowed_mechs = cfg->allowed_mechs;
430     } else {
431         /* Try to fetch the default set if not explicitly configured,
432          * We need to do this because gss_acquire_cred_with_password()
433          * is currently limited to acquire creds for a single "default"
434          * mechanism if no desired mechanisms are passed in. This causes
435          * authentication to fail for secondary mechanisms as no user
436          * credentials are generated for those. */
437         maj = gss_indicate_mechs(&min, &indicated_mechs);
438         if (maj != GSS_S_COMPLETE) {
439             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "%s",
440                           mag_error(req, "gss_indicate_mechs() failed",
441                                     maj, min));
442             /* if indicated _mechs failed, set GSS_C_NO_OID_SET. This
443              * generally causes only the krb5 mechanism to be tried due
444              * to implementation constraints, but may change in future. */
445             allowed_mechs = GSS_C_NO_OID_SET;
446         } else {
447             allowed_mechs = indicated_mechs;
448         }
449     }
450
451     /* Remove Spnego if present, or we'd repeat failed authentiations
452      * multiple times, one within Spnego and then again with an explicit
453      * mechanism. We would normally just force Spnego and use
454      * gss_set_neg_mechs, but due to the way we source the server name
455      * and the fact MIT up to 1.14 at least does no handle union names,
456      * we can't provide spnego with a server name that can be used by
457      * multiple mechanisms, causing any but the first mechanism to fail.
458      * Also remove unwanted krb mechs, or AS requests will be repeated
459      * multiple times uselessly.
460      */
461     filtered_mechs = mag_filter_unwanted_mechs(allowed_mechs);
462     if (filtered_mechs == GSS_C_NO_OID_SET) {
463         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "Fatal "
464                       "failure while filtering mechs, aborting");
465         goto done;
466     } else if (filtered_mechs != allowed_mechs) {
467         /* if indicated_mechs where sourced then free them here before
468          * reusing the pointer */
469         gss_release_oid_set(&min, &indicated_mechs);
470
471         /* mark the list of mechs needs to be freed */
472         indicated_mechs = filtered_mechs;
473
474         /* use the filtered list */
475         allowed_mechs = filtered_mechs;
476     }
477
478 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
479     /* If we are using the krb5 mechanism make sure to set a per thread
480      * memory ccache so that there can't be interferences between threads.
481      * Also make sure we have  new cache so no cached results end up being
482      * used. Some implementations of gss_acquire_cred_with_password() do
483      * not reacquire creds if cached ones are around, failing to check
484      * again for the password. */
485     maj = gss_test_oid_set_member(&min, discard_const(gss_mech_krb5),
486                                   allowed_mechs, &present);
487     if (GSS_ERROR(maj)) {
488         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
489                       "In Basic Auth, %s",
490                       mag_error(req, "gss_test_oid_set_member() failed",
491                                 maj, min));
492         goto done;
493     }
494     if (present) {
495         rs = apr_generate_random_bytes((unsigned char *)(&rndname),
496                                        sizeof(long long unsigned int));
497         if (rs != APR_SUCCESS) {
498             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
499                           "Failed to generate random ccache name");
500             goto done;
501         }
502         user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
503         maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
504         if (GSS_ERROR(maj)) {
505             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
506                           "In Basic Auth, %s",
507                           mag_error(req, "gss_krb5_ccache_name() "
508                                     "failed", maj, min));
509             goto done;
510         }
511     }
512 #endif
513
514     maj = gss_acquire_cred_with_password(&min, user, &ba_pwd,
515                                          GSS_C_INDEFINITE,
516                                          allowed_mechs,
517                                          GSS_C_INITIATE,
518                                          &user_cred, &actual_mechs, NULL);
519     if (GSS_ERROR(maj)) {
520         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
521                       "In Basic Auth, %s",
522                       mag_error(req, "gss_acquire_cred_with_password() "
523                                 "failed", maj, min));
524         goto done;
525     }
526
527     /* must acquire creds based on the actual mechs we want to try */
528     if (!mag_acquire_creds(req, cfg, actual_mechs,
529                            cred_usage, &acquired_cred, NULL)) {
530         goto done;
531     }
532
533     if (cred_usage == GSS_C_BOTH) {
534         /* must acquire with GSS_C_ACCEPT to get the server name */
535         if (!mag_acquire_creds(req, cfg, actual_mechs,
536                                GSS_C_ACCEPT, &server_cred, NULL)) {
537             goto done;
538         }
539     } else {
540         server_cred = acquired_cred;
541     }
542
543 #ifdef HAVE_CRED_STORE
544     if (cfg->deleg_ccache_dir) {
545         /* delegate ourselves credentials so we store them as requested */
546         init_flags |= GSS_C_DELEG_FLAG;
547     }
548 #endif
549
550     for (int i = 0; i < actual_mechs->count; i++) {
551
552         /* free these if looping */
553         gss_release_buffer(&min, &output);
554         gss_release_buffer(&min, &input);
555         gss_release_name(&min, &server);
556
557         maj = gss_inquire_cred_by_mech(&min, server_cred,
558                                        &actual_mechs->elements[i],
559                                        &server, NULL, NULL, NULL);
560         if (GSS_ERROR(maj)) {
561             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
562                           "%s", mag_error(req, "gss_inquired_cred_by_mech() "
563                                           "failed", maj, min));
564             continue;
565         }
566
567         do {
568             /* output and input are inverted here, this is intentional */
569             maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
570                                        &actual_mechs->elements[i], init_flags,
571                                        300, GSS_C_NO_CHANNEL_BINDINGS, &output,
572                                        NULL, &input, NULL, NULL);
573             if (GSS_ERROR(maj)) {
574                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
575                               "%s", mag_error(req, "gss_init_sec_context() "
576                                               "failed", maj, min));
577                 break;
578             }
579             gss_release_buffer(&min, &output);
580             maj = gss_accept_sec_context(&min, &server_ctx, acquired_cred,
581                                          &input, GSS_C_NO_CHANNEL_BINDINGS,
582                                          client, mech_type, &output, NULL,
583                                          vtime, delegated_cred);
584             if (GSS_ERROR(maj)) {
585                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
586                               "%s", mag_error(req, "gss_accept_sec_context()"
587                                               " failed", maj, min));
588                 break;
589             }
590             gss_release_buffer(&min, &input);
591         } while (maj == GSS_S_CONTINUE_NEEDED);
592
593         if (maj == GSS_S_COMPLETE) {
594             ret = true;
595             break;
596         }
597     }
598
599 done:
600     gss_release_buffer(&min, &output);
601     gss_release_buffer(&min, &input);
602     gss_release_name(&min, &server);
603     if (server_cred != acquired_cred)
604         gss_release_cred(&min, &server_cred);
605     gss_delete_sec_context(&min, &server_ctx, GSS_C_NO_BUFFER);
606     gss_release_cred(&min, &acquired_cred);
607     gss_release_name(&min, &user);
608     gss_release_cred(&min, &user_cred);
609     gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER);
610     gss_release_oid_set(&min, &actual_mechs);
611     gss_release_oid_set(&min, &indicated_mechs);
612 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
613     if (user_ccache != NULL) {
614         maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
615         if (maj != GSS_S_COMPLETE) {
616             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
617                           "Failed to restore per-thread ccache, %s",
618                           mag_error(req, "gss_krb5_ccache_name() "
619                                     "failed", maj, min));
620         }
621     }
622 #endif
623     return ret;
624 }
625
626
627 static int mag_auth(request_rec *req)
628 {
629     const char *type;
630     int auth_type = -1;
631     struct mag_config *cfg;
632     const char *auth_header;
633     char *auth_header_type;
634     int ret = HTTP_UNAUTHORIZED;
635     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
636     gss_ctx_id_t *pctx;
637     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
638     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
639     gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
640     gss_buffer_desc ba_user;
641     gss_buffer_desc ba_pwd;
642     gss_name_t client = GSS_C_NO_NAME;
643     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
644     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
645     gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
646     uint32_t vtime;
647     uint32_t maj, min;
648     char *reply;
649     size_t replen;
650     char *clientname;
651     gss_OID mech_type = GSS_C_NO_OID;
652     gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
653     gss_OID_set indicated_mechs = GSS_C_NO_OID_SET;
654     gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
655     struct mag_conn *mc = NULL;
656     time_t expiration;
657     int i;
658
659     type = ap_auth_type(req);
660     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
661         return DECLINED;
662     }
663
664     cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
665
666     if (cfg->allowed_mechs) {
667         desired_mechs = cfg->allowed_mechs;
668     } else {
669         /* Try to fetch the default set if not explicitly configured */
670         maj = gss_indicate_mechs(&min, &indicated_mechs);
671         if (maj != GSS_S_COMPLETE) {
672             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "%s",
673                           mag_error(req, "gss_indicate_mechs() failed",
674                                     maj, min));
675         }
676         desired_mechs = indicated_mechs;
677     }
678
679     /* implicit auth for subrequests if main auth already happened */
680     if (!ap_is_initial_req(req) && req->main != NULL) {
681         type = ap_auth_type(req->main);
682         if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
683             /* warn if the subrequest location and the main request
684              * location have different configs */
685             if (cfg != ap_get_module_config(req->main->per_dir_config,
686                                             &auth_gssapi_module)) {
687                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
688                               req, "Subrequest authentication bypass on "
689                                    "location with different configuration!");
690             }
691             if (req->main->user) {
692                 req->user = apr_pstrdup(req->pool, req->main->user);
693                 return OK;
694             } else {
695                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
696                               "The main request is tasked to establish the "
697                               "security context, can't proceed!");
698                 return HTTP_UNAUTHORIZED;
699             }
700         } else {
701             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
702                           "Subrequest GSSAPI auth with no auth on the main "
703                           "request. This operation may fail if other "
704                           "subrequests already established a context or the "
705                           "mechanism requires multiple roundtrips.");
706         }
707     }
708
709     if (cfg->ssl_only) {
710         if (!mag_conn_is_https(req->connection)) {
711             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
712                           "Not a TLS connection, refusing to authenticate!");
713             goto done;
714         }
715     }
716
717     if (cfg->gss_conn_ctx) {
718         mc = (struct mag_conn *)ap_get_module_config(
719                                                 req->connection->conn_config,
720                                                 &auth_gssapi_module);
721         if (!mc) {
722             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
723                           "Failed to retrieve connection context!");
724             goto done;
725         }
726     }
727
728     /* if available, session always supersedes connection bound data */
729     if (cfg->use_sessions) {
730         mag_check_session(req, cfg, &mc);
731     }
732
733     auth_header = apr_table_get(req->headers_in, "Authorization");
734
735     if (mc) {
736         if (mc->established &&
737             (auth_header == NULL) &&
738             (mc->auth_type != AUTH_TYPE_BASIC)) {
739             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
740                           "Already established context found!");
741             mag_set_req_data(req, cfg, mc);
742             ret = OK;
743             goto done;
744         }
745         pctx = &mc->ctx;
746     } else {
747         pctx = &ctx;
748     }
749
750     /* We can proceed only if we do have an auth header */
751     if (!auth_header) goto done;
752
753     auth_header_type = ap_getword_white(req->pool, &auth_header);
754     if (!auth_header_type) goto done;
755
756     for (i = 0; auth_types[i] != NULL; i++) {
757         if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
758             auth_type = i;
759             break;
760         }
761     }
762
763     switch (auth_type) {
764     case AUTH_TYPE_NEGOTIATE:
765         if (!parse_auth_header(req->pool, &auth_header, &input)) {
766             goto done;
767         }
768         break;
769     case AUTH_TYPE_BASIC:
770         if (!cfg->use_basic_auth) {
771             goto done;
772         }
773
774         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
775         if (!ba_pwd.value) goto done;
776         ba_user.value = ap_getword_nulls_nc(req->pool,
777                                             (char **)&ba_pwd.value, ':');
778         if (!ba_user.value) goto done;
779         if (((char *)ba_user.value)[0] == '\0' ||
780             ((char *)ba_pwd.value)[0] == '\0') {
781             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
782                           "Invalid empty user or password for Basic Auth");
783             goto done;
784         }
785         ba_user.length = strlen(ba_user.value);
786         ba_pwd.length = strlen(ba_pwd.value);
787
788         if (mc && mc->established &&
789             mag_basic_check(cfg, mc, ba_user, ba_pwd)) {
790             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
791                           "Already established BASIC AUTH context found!");
792             mag_set_req_data(req, cfg, mc);
793             ret = OK;
794             goto done;
795         }
796
797         break;
798
799     case AUTH_TYPE_RAW_NTLM:
800         if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
801             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
802                           "NTLM Authentication is not allowed!");
803             goto done;
804         }
805
806         if (!parse_auth_header(req->pool, &auth_header, &input)) {
807             goto done;
808         }
809
810         desired_mechs = discard_const(&gss_mech_set_ntlmssp);
811         break;
812
813     default:
814         goto done;
815     }
816
817     if (mc && mc->established) {
818         /* if we are re-authenticating make sure the conn context
819          * is cleaned up so we do not accidentally reuse an existing
820          * established context */
821         mag_conn_clear(mc);
822     }
823
824     req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
825
826 #ifdef HAVE_CRED_STORE
827     if (cfg->use_s4u2proxy) {
828         cred_usage = GSS_C_BOTH;
829     }
830 #endif
831
832     if (auth_type == AUTH_TYPE_BASIC) {
833         if (mag_auth_basic(req, cfg, ba_user, ba_pwd,
834                            cred_usage, &client, &mech_type,
835                            &delegated_cred, &vtime)) {
836             goto complete;
837         }
838         goto done;
839     }
840
841     if (!mag_acquire_creds(req, cfg, desired_mechs,
842                            cred_usage, &acquired_cred, NULL)) {
843         goto done;
844     }
845
846     if (auth_type == AUTH_TYPE_NEGOTIATE &&
847         cfg->allowed_mechs != GSS_C_NO_OID_SET) {
848         maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
849         if (GSS_ERROR(maj)) {
850             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
851                           mag_error(req, "gss_set_neg_mechs() failed",
852                                     maj, min));
853             goto done;
854         }
855     }
856
857     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
858                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
859                                  &client, &mech_type, &output, NULL, &vtime,
860                                  &delegated_cred);
861     if (GSS_ERROR(maj)) {
862         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
863                       mag_error(req, "gss_accept_sec_context() failed",
864                                 maj, min));
865         goto done;
866     } else if (maj == GSS_S_CONTINUE_NEEDED) {
867         if (!mc) {
868             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
869                           "Mechanism needs continuation but neither "
870                           "GssapiConnectionBound nor "
871                           "GssapiUseSessions are available");
872             gss_release_buffer(&min, &output);
873             output.length = 0;
874         }
875         /* auth not complete send token and wait next packet */
876         goto done;
877     }
878
879 complete:
880     /* Always set the GSS name in an env var */
881     maj = gss_display_name(&min, client, &name, NULL);
882     if (GSS_ERROR(maj)) {
883         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
884                       mag_error(req, "gss_display_name() failed",
885                                 maj, min));
886         goto done;
887     }
888     clientname = apr_pstrndup(req->pool, name.value, name.length);
889     apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
890     expiration = time(NULL) + vtime;
891     apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
892                   apr_psprintf(req->pool, "%ld", (long)expiration));
893
894 #ifdef HAVE_CRED_STORE
895     if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
896         char *ccachefile = NULL;
897
898         mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
899                               delegated_cred, &ccachefile);
900
901         if (ccachefile) {
902             mag_set_KRB5CCANME(req, ccachefile);
903         }
904
905         if (mc) {
906             mc->delegated = true;
907         }
908     }
909 #endif
910
911     if (cfg->map_to_local) {
912         maj = gss_localname(&min, client, mech_type, &lname);
913         if (maj != GSS_S_COMPLETE) {
914             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
915                           mag_error(req, "gss_localname() failed", maj, min));
916             goto done;
917         }
918         req->user = apr_pstrndup(req->pool, lname.value, lname.length);
919     } else {
920         req->user = clientname;
921     }
922
923     if (mc) {
924         mc->user_name = apr_pstrdup(mc->pool, req->user);
925         mc->gss_name = apr_pstrdup(mc->pool, clientname);
926         mc->established = true;
927         if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
928             vtime = MIN_SESS_EXP_TIME;
929         }
930         mc->expiration = expiration;
931         mc->auth_type = auth_type;
932         if (auth_type == AUTH_TYPE_BASIC) {
933             mag_basic_cache(cfg, mc, ba_user, ba_pwd);
934         }
935         if (cfg->use_sessions) {
936             mag_attempt_session(req, cfg, mc);
937         }
938     }
939
940     if (cfg->send_persist)
941         apr_table_set(req->headers_out, "Persistent-Auth",
942             cfg->gss_conn_ctx ? "true" : "false");
943
944     ret = OK;
945
946 done:
947     if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
948         int prefixlen = strlen(auth_types[auth_type]) + 1;
949         replen = apr_base64_encode_len(output.length) + 1;
950         reply = apr_pcalloc(req->pool, prefixlen + replen);
951         if (reply) {
952             memcpy(reply, auth_types[auth_type], prefixlen - 1);
953             reply[prefixlen - 1] = ' ';
954             apr_base64_encode(&reply[prefixlen], output.value, output.length);
955             apr_table_add(req->err_headers_out,
956                           "WWW-Authenticate", reply);
957         }
958     } else if (ret == HTTP_UNAUTHORIZED) {
959         apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
960         if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
961             apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
962         }
963         if (cfg->use_basic_auth) {
964             apr_table_add(req->err_headers_out,
965                           "WWW-Authenticate",
966                           apr_psprintf(req->pool, "Basic realm=\"%s\"",
967                                        ap_auth_name(req)));
968         }
969     }
970     gss_release_oid_set(&min, &indicated_mechs);
971     if (ctx != GSS_C_NO_CONTEXT)
972         gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
973     gss_release_cred(&min, &acquired_cred);
974     gss_release_cred(&min, &delegated_cred);
975     gss_release_buffer(&min, &output);
976     gss_release_name(&min, &client);
977     gss_release_buffer(&min, &name);
978     gss_release_buffer(&min, &lname);
979     return ret;
980 }
981
982
983 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
984 {
985     struct mag_config *cfg;
986
987     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
988     cfg->pool = p;
989
990     return cfg;
991 }
992
993 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
994 {
995     struct mag_config *cfg = (struct mag_config *)mconfig;
996     cfg->ssl_only = on ? true : false;
997     return NULL;
998 }
999
1000 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
1001 {
1002     struct mag_config *cfg = (struct mag_config *)mconfig;
1003     cfg->map_to_local = on ? true : false;
1004     return NULL;
1005 }
1006
1007 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
1008 {
1009     struct mag_config *cfg = (struct mag_config *)mconfig;
1010     cfg->gss_conn_ctx = on ? true : false;
1011     return NULL;
1012 }
1013
1014 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
1015 {
1016     struct mag_config *cfg = (struct mag_config *)mconfig;
1017     cfg->send_persist = on ? true : false;
1018     return NULL;
1019 }
1020
1021 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
1022 {
1023     struct mag_config *cfg = (struct mag_config *)mconfig;
1024     cfg->use_sessions = on ? true : false;
1025     return NULL;
1026 }
1027
1028 #ifdef HAVE_CRED_STORE
1029 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
1030 {
1031     struct mag_config *cfg = (struct mag_config *)mconfig;
1032     cfg->use_s4u2proxy = on ? true : false;
1033
1034     if (cfg->deleg_ccache_dir == NULL) {
1035         cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
1036     }
1037     return NULL;
1038 }
1039 #endif
1040
1041 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
1042 {
1043     struct mag_config *cfg = (struct mag_config *)mconfig;
1044     struct databuf keys;
1045     unsigned char *val;
1046     apr_status_t rc;
1047     const char *k;
1048     int l;
1049
1050     if (strncmp(w, "key:", 4) != 0) {
1051         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1052                      "Invalid key format, expected prefix 'key:'");
1053         return NULL;
1054     }
1055     k = w + 4;
1056
1057     l = apr_base64_decode_len(k);
1058     val = apr_palloc(parms->temp_pool, l);
1059
1060     keys.length = (int)apr_base64_decode_binary(val, k);
1061     keys.value = (unsigned char *)val;
1062
1063     if (keys.length != 32) {
1064         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1065                      "Invalid key length, expected 32 got %d", keys.length);
1066         return NULL;
1067     }
1068
1069     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
1070     if (rc != OK) {
1071         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1072                      "Failed to import sealing key!");
1073     }
1074     return NULL;
1075 }
1076
1077 #ifdef HAVE_CRED_STORE
1078
1079 #define MAX_CRED_OPTIONS 10
1080
1081 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
1082                                   const char *w)
1083 {
1084     struct mag_config *cfg = (struct mag_config *)mconfig;
1085     gss_key_value_element_desc *elements;
1086     uint32_t count;
1087     size_t size;
1088     const char *p;
1089     char *value;
1090     char *key;
1091
1092     p = strchr(w, ':');
1093     if (!p) {
1094         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1095                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
1096         return NULL;
1097     }
1098
1099     key = apr_pstrndup(parms->pool, w, (p-w));
1100     value = apr_pstrdup(parms->pool, p + 1);
1101
1102     if (!cfg->cred_store) {
1103         cfg->cred_store = apr_pcalloc(parms->pool,
1104                                       sizeof(gss_key_value_set_desc));
1105         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
1106         cfg->cred_store->elements = apr_palloc(parms->pool, size);
1107     }
1108
1109     elements = cfg->cred_store->elements;
1110     count = cfg->cred_store->count;
1111
1112     if (count >= MAX_CRED_OPTIONS) {
1113         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1114                      "Too many GssapiCredStore options (MAX: %d)",
1115                      MAX_CRED_OPTIONS);
1116         return NULL;
1117     }
1118     cfg->cred_store->count++;
1119
1120     elements[count].key = key;
1121     elements[count].value = value;
1122
1123     return NULL;
1124 }
1125
1126 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
1127                                         const char *value)
1128 {
1129     struct mag_config *cfg = (struct mag_config *)mconfig;
1130
1131     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
1132
1133     return NULL;
1134 }
1135 #endif
1136
1137 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1138 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
1139 {
1140     struct mag_config *cfg = (struct mag_config *)mconfig;
1141
1142     cfg->use_basic_auth = on ? true : false;
1143     return NULL;
1144 }
1145 #endif
1146
1147 static apr_status_t mag_oid_set_destroy(void *ptr)
1148 {
1149     uint32_t min;
1150     gss_OID_set set = (gss_OID_set)ptr;
1151     (void)gss_release_oid_set(&min, &set);
1152     return APR_SUCCESS;
1153 }
1154
1155 static void mag_list_of_mechs(cmd_parms *parms, gss_OID_set *oidset,
1156                               bool add_spnego, const char *w)
1157 {
1158     gss_buffer_desc buf = { 0 };
1159     uint32_t maj, min;
1160     gss_OID_set set;
1161     gss_OID oid;
1162     bool release_oid = false;
1163
1164     if (NULL == *oidset) {
1165         maj = gss_create_empty_oid_set(&min, &set);
1166         if (maj != GSS_S_COMPLETE) {
1167             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1168                          "gss_create_empty_oid_set() failed.");
1169             *oidset = GSS_C_NO_OID_SET;
1170             return;
1171         }
1172         if (add_spnego) {
1173             oid = discard_const(&gss_mech_spnego);
1174             maj = gss_add_oid_set_member(&min, oid, &set);
1175             if (maj != GSS_S_COMPLETE) {
1176                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1177                              "gss_add_oid_set_member() failed.");
1178                 (void)gss_release_oid_set(&min, &set);
1179                 *oidset = GSS_C_NO_OID_SET;
1180                 return;
1181             }
1182         }
1183         /* register in the pool so it can be released once the server
1184          * winds down */
1185         apr_pool_cleanup_register(parms->pool, (void *)set,
1186                                   mag_oid_set_destroy,
1187                                   apr_pool_cleanup_null);
1188         *oidset = set;
1189     } else {
1190         set = *oidset;
1191     }
1192
1193     if (strcmp(w, "krb5") == 0) {
1194         oid = discard_const(gss_mech_krb5);
1195     } else if (strcmp(w, "iakerb") == 0) {
1196         oid = discard_const(gss_mech_iakerb);
1197     } else if (strcmp(w, "ntlmssp") == 0) {
1198         oid = discard_const(&gss_mech_ntlmssp);
1199     } else {
1200         buf.value = discard_const(w);
1201         buf.length = strlen(w);
1202         maj = gss_str_to_oid(&min, &buf, &oid);
1203         if (maj != GSS_S_COMPLETE) {
1204             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1205                          "Unrecognized GSSAPI Mechanism: [%s]", w);
1206             return;
1207         }
1208         release_oid = true;
1209     }
1210     maj = gss_add_oid_set_member(&min, oid, &set);
1211     if (maj != GSS_S_COMPLETE) {
1212         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1213                          "gss_add_oid_set_member() failed for [%s].", w);
1214     }
1215     if (release_oid) {
1216         (void)gss_release_oid(&min, &oid);
1217     }
1218 }
1219
1220 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
1221                                   const char *w)
1222 {
1223     struct mag_config *cfg = (struct mag_config *)mconfig;
1224
1225     mag_list_of_mechs(parms, &cfg->allowed_mechs, true, w);
1226
1227     return NULL;
1228 }
1229
1230 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1231 static const char *mag_basic_auth_mechs(cmd_parms *parms, void *mconfig,
1232                                         const char *w)
1233 {
1234     struct mag_config *cfg = (struct mag_config *)mconfig;
1235
1236     mag_list_of_mechs(parms, &cfg->basic_mechs, false, w);
1237
1238     return NULL;
1239 }
1240 #endif
1241
1242 static const command_rec mag_commands[] = {
1243     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
1244                   "Work only if connection is SSL Secured"),
1245     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
1246                   "Translate principals to local names"),
1247     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
1248                   "Authentication is bound to the TCP connection"),
1249     AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
1250                   "Send Persitent-Auth header according to connection bound"),
1251     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
1252                   "Authentication uses mod_sessions to hold status"),
1253     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
1254                      "Key Used to seal session data."),
1255 #ifdef HAVE_CRED_STORE
1256     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
1257                   "Initializes credentials for s4u2proxy usage"),
1258     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
1259                     "Credential Store"),
1260     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
1261                      OR_AUTHCFG, "Directory to store delegated credentials"),
1262 #endif
1263 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1264     AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
1265                      "Allows use of Basic Auth for authentication"),
1266     AP_INIT_ITERATE("GssapiBasicAuthMech", mag_basic_auth_mechs, NULL,
1267                     OR_AUTHCFG, "Mechanisms to use for basic auth"),
1268 #endif
1269     AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
1270                     "Allowed Mechanisms"),
1271     { NULL }
1272 };
1273
1274 static void
1275 mag_register_hooks(apr_pool_t *p)
1276 {
1277     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1278     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1279     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1280 }
1281
1282 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1283 {
1284     STANDARD20_MODULE_STUFF,
1285     mag_create_dir_config,
1286     NULL,
1287     NULL,
1288     NULL,
1289     mag_commands,
1290     mag_register_hooks
1291 };