38966499f115a065ed7d81645ee2ae16d58f844f
[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         /* skip spnego if present */
553         if (gss_oid_equal(&actual_mechs->elements[i],
554                           &gss_mech_spnego)) {
555             continue;
556         }
557
558         /* free these if looping */
559         gss_release_buffer(&min, &output);
560         gss_release_buffer(&min, &input);
561         gss_release_name(&min, &server);
562
563         maj = gss_inquire_cred_by_mech(&min, server_cred,
564                                        &actual_mechs->elements[i],
565                                        &server, NULL, NULL, NULL);
566         if (GSS_ERROR(maj)) {
567             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
568                           "%s", mag_error(req, "gss_inquired_cred_by_mech() "
569                                           "failed", maj, min));
570             continue;
571         }
572
573         do {
574             /* output and input are inverted here, this is intentional */
575             maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
576                                        &actual_mechs->elements[i], init_flags,
577                                        300, GSS_C_NO_CHANNEL_BINDINGS, &output,
578                                        NULL, &input, NULL, NULL);
579             if (GSS_ERROR(maj)) {
580                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
581                               "%s", mag_error(req, "gss_init_sec_context() "
582                                               "failed", maj, min));
583                 break;
584             }
585             gss_release_buffer(&min, &output);
586             maj = gss_accept_sec_context(&min, &server_ctx, acquired_cred,
587                                          &input, GSS_C_NO_CHANNEL_BINDINGS,
588                                          client, mech_type, &output, NULL,
589                                          vtime, delegated_cred);
590             if (GSS_ERROR(maj)) {
591                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
592                               "%s", mag_error(req, "gss_accept_sec_context()"
593                                               " failed", maj, min));
594                 break;
595             }
596             gss_release_buffer(&min, &input);
597         } while (maj == GSS_S_CONTINUE_NEEDED);
598
599         if (maj == GSS_S_COMPLETE) {
600             ret = true;
601             break;
602         }
603     }
604
605 done:
606     gss_release_buffer(&min, &output);
607     gss_release_buffer(&min, &input);
608     gss_release_name(&min, &server);
609     if (server_cred != acquired_cred)
610         gss_release_cred(&min, &server_cred);
611     gss_delete_sec_context(&min, &server_ctx, GSS_C_NO_BUFFER);
612     gss_release_cred(&min, &acquired_cred);
613     gss_release_name(&min, &user);
614     gss_release_cred(&min, &user_cred);
615     gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER);
616     gss_release_oid_set(&min, &actual_mechs);
617     gss_release_oid_set(&min, &indicated_mechs);
618 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
619     if (user_ccache != NULL) {
620         maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
621         if (maj != GSS_S_COMPLETE) {
622             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
623                           "Failed to restore per-thread ccache, %s",
624                           mag_error(req, "gss_krb5_ccache_name() "
625                                     "failed", maj, min));
626         }
627     }
628 #endif
629     return ret;
630 }
631
632
633 static int mag_auth(request_rec *req)
634 {
635     const char *type;
636     int auth_type = -1;
637     struct mag_config *cfg;
638     const char *auth_header;
639     char *auth_header_type;
640     int ret = HTTP_UNAUTHORIZED;
641     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
642     gss_ctx_id_t *pctx;
643     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
644     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
645     gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
646     gss_buffer_desc ba_user;
647     gss_buffer_desc ba_pwd;
648     gss_name_t client = GSS_C_NO_NAME;
649     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
650     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
651     gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
652     uint32_t vtime;
653     uint32_t maj, min;
654     char *reply;
655     size_t replen;
656     char *clientname;
657     gss_OID mech_type = GSS_C_NO_OID;
658     gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
659     gss_OID_set indicated_mechs = GSS_C_NO_OID_SET;
660     gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
661     struct mag_conn *mc = NULL;
662     time_t expiration;
663     int i;
664
665     type = ap_auth_type(req);
666     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
667         return DECLINED;
668     }
669
670     cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
671
672     if (cfg->allowed_mechs) {
673         desired_mechs = cfg->allowed_mechs;
674     } else {
675         /* Try to fetch the default set if not explicitly configured */
676         maj = gss_indicate_mechs(&min, &indicated_mechs);
677         if (maj != GSS_S_COMPLETE) {
678             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "%s",
679                           mag_error(req, "gss_indicate_mechs() failed",
680                                     maj, min));
681         }
682         desired_mechs = indicated_mechs;
683     }
684
685     /* implicit auth for subrequests if main auth already happened */
686     if (!ap_is_initial_req(req) && req->main != NULL) {
687         type = ap_auth_type(req->main);
688         if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
689             /* warn if the subrequest location and the main request
690              * location have different configs */
691             if (cfg != ap_get_module_config(req->main->per_dir_config,
692                                             &auth_gssapi_module)) {
693                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
694                               req, "Subrequest authentication bypass on "
695                                    "location with different configuration!");
696             }
697             if (req->main->user) {
698                 req->user = apr_pstrdup(req->pool, req->main->user);
699                 return OK;
700             } else {
701                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
702                               "The main request is tasked to establish the "
703                               "security context, can't proceed!");
704                 return HTTP_UNAUTHORIZED;
705             }
706         } else {
707             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
708                           "Subrequest GSSAPI auth with no auth on the main "
709                           "request. This operation may fail if other "
710                           "subrequests already established a context or the "
711                           "mechanism requires multiple roundtrips.");
712         }
713     }
714
715     if (cfg->ssl_only) {
716         if (!mag_conn_is_https(req->connection)) {
717             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
718                           "Not a TLS connection, refusing to authenticate!");
719             goto done;
720         }
721     }
722
723     if (cfg->gss_conn_ctx) {
724         mc = (struct mag_conn *)ap_get_module_config(
725                                                 req->connection->conn_config,
726                                                 &auth_gssapi_module);
727         if (!mc) {
728             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
729                           "Failed to retrieve connection context!");
730             goto done;
731         }
732     }
733
734     /* if available, session always supersedes connection bound data */
735     if (cfg->use_sessions) {
736         mag_check_session(req, cfg, &mc);
737     }
738
739     auth_header = apr_table_get(req->headers_in, "Authorization");
740
741     if (mc) {
742         if (mc->established &&
743             (auth_header == NULL) &&
744             (mc->auth_type != AUTH_TYPE_BASIC)) {
745             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
746                           "Already established context found!");
747             mag_set_req_data(req, cfg, mc);
748             ret = OK;
749             goto done;
750         }
751         pctx = &mc->ctx;
752     } else {
753         pctx = &ctx;
754     }
755
756     /* We can proceed only if we do have an auth header */
757     if (!auth_header) goto done;
758
759     auth_header_type = ap_getword_white(req->pool, &auth_header);
760     if (!auth_header_type) goto done;
761
762     for (i = 0; auth_types[i] != NULL; i++) {
763         if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
764             auth_type = i;
765             break;
766         }
767     }
768
769     switch (auth_type) {
770     case AUTH_TYPE_NEGOTIATE:
771         if (!parse_auth_header(req->pool, &auth_header, &input)) {
772             goto done;
773         }
774         break;
775     case AUTH_TYPE_BASIC:
776         if (!cfg->use_basic_auth) {
777             goto done;
778         }
779
780         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
781         if (!ba_pwd.value) goto done;
782         ba_user.value = ap_getword_nulls_nc(req->pool,
783                                             (char **)&ba_pwd.value, ':');
784         if (!ba_user.value) goto done;
785         if (((char *)ba_user.value)[0] == '\0' ||
786             ((char *)ba_pwd.value)[0] == '\0') {
787             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
788                           "Invalid empty user or password for Basic Auth");
789             goto done;
790         }
791         ba_user.length = strlen(ba_user.value);
792         ba_pwd.length = strlen(ba_pwd.value);
793
794         if (mc && mc->established &&
795             mag_basic_check(cfg, mc, ba_user, ba_pwd)) {
796             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
797                           "Already established BASIC AUTH context found!");
798             mag_set_req_data(req, cfg, mc);
799             ret = OK;
800             goto done;
801         }
802
803         break;
804
805     case AUTH_TYPE_RAW_NTLM:
806         if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
807             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
808                           "NTLM Authentication is not allowed!");
809             goto done;
810         }
811
812         if (!parse_auth_header(req->pool, &auth_header, &input)) {
813             goto done;
814         }
815
816         desired_mechs = discard_const(&gss_mech_set_ntlmssp);
817         break;
818
819     default:
820         goto done;
821     }
822
823     if (mc && mc->established) {
824         /* if we are re-authenticating make sure the conn context
825          * is cleaned up so we do not accidentally reuse an existing
826          * established context */
827         mag_conn_clear(mc);
828     }
829
830     req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
831
832 #ifdef HAVE_CRED_STORE
833     if (cfg->use_s4u2proxy) {
834         cred_usage = GSS_C_BOTH;
835     }
836 #endif
837
838     if (auth_type == AUTH_TYPE_BASIC) {
839         if (mag_auth_basic(req, cfg, ba_user, ba_pwd,
840                            cred_usage, &client, &mech_type,
841                            &delegated_cred, &vtime)) {
842             goto complete;
843         }
844         goto done;
845     }
846
847     if (!mag_acquire_creds(req, cfg, desired_mechs,
848                            cred_usage, &acquired_cred, NULL)) {
849         goto done;
850     }
851
852     if (auth_type == AUTH_TYPE_NEGOTIATE &&
853         cfg->allowed_mechs != GSS_C_NO_OID_SET) {
854         maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
855         if (GSS_ERROR(maj)) {
856             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
857                           mag_error(req, "gss_set_neg_mechs() failed",
858                                     maj, min));
859             goto done;
860         }
861     }
862
863     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
864                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
865                                  &client, &mech_type, &output, NULL, &vtime,
866                                  &delegated_cred);
867     if (GSS_ERROR(maj)) {
868         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
869                       mag_error(req, "gss_accept_sec_context() failed",
870                                 maj, min));
871         goto done;
872     } else if (maj == GSS_S_CONTINUE_NEEDED) {
873         if (!mc) {
874             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
875                           "Mechanism needs continuation but neither "
876                           "GssapiConnectionBound nor "
877                           "GssapiUseSessions are available");
878             gss_release_buffer(&min, &output);
879             output.length = 0;
880         }
881         /* auth not complete send token and wait next packet */
882         goto done;
883     }
884
885 complete:
886     /* Always set the GSS name in an env var */
887     maj = gss_display_name(&min, client, &name, NULL);
888     if (GSS_ERROR(maj)) {
889         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
890                       mag_error(req, "gss_display_name() failed",
891                                 maj, min));
892         goto done;
893     }
894     clientname = apr_pstrndup(req->pool, name.value, name.length);
895     apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
896     expiration = time(NULL) + vtime;
897     apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
898                   apr_psprintf(req->pool, "%ld", (long)expiration));
899
900 #ifdef HAVE_CRED_STORE
901     if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
902         char *ccachefile = NULL;
903
904         mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
905                               delegated_cred, &ccachefile);
906
907         if (ccachefile) {
908             mag_set_KRB5CCANME(req, ccachefile);
909         }
910
911         if (mc) {
912             mc->delegated = true;
913         }
914     }
915 #endif
916
917     if (cfg->map_to_local) {
918         maj = gss_localname(&min, client, mech_type, &lname);
919         if (maj != GSS_S_COMPLETE) {
920             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
921                           mag_error(req, "gss_localname() failed", maj, min));
922             goto done;
923         }
924         req->user = apr_pstrndup(req->pool, lname.value, lname.length);
925     } else {
926         req->user = clientname;
927     }
928
929     if (mc) {
930         mc->user_name = apr_pstrdup(mc->pool, req->user);
931         mc->gss_name = apr_pstrdup(mc->pool, clientname);
932         mc->established = true;
933         if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
934             vtime = MIN_SESS_EXP_TIME;
935         }
936         mc->expiration = expiration;
937         mc->auth_type = auth_type;
938         if (auth_type == AUTH_TYPE_BASIC) {
939             mag_basic_cache(cfg, mc, ba_user, ba_pwd);
940         }
941         if (cfg->use_sessions) {
942             mag_attempt_session(req, cfg, mc);
943         }
944     }
945
946     if (cfg->send_persist)
947         apr_table_set(req->headers_out, "Persistent-Auth",
948             cfg->gss_conn_ctx ? "true" : "false");
949
950     ret = OK;
951
952 done:
953     if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
954         int prefixlen = strlen(auth_types[auth_type]) + 1;
955         replen = apr_base64_encode_len(output.length) + 1;
956         reply = apr_pcalloc(req->pool, prefixlen + replen);
957         if (reply) {
958             memcpy(reply, auth_types[auth_type], prefixlen - 1);
959             reply[prefixlen - 1] = ' ';
960             apr_base64_encode(&reply[prefixlen], output.value, output.length);
961             apr_table_add(req->err_headers_out,
962                           "WWW-Authenticate", reply);
963         }
964     } else if (ret == HTTP_UNAUTHORIZED) {
965         apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
966         if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
967             apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
968         }
969         if (cfg->use_basic_auth) {
970             apr_table_add(req->err_headers_out,
971                           "WWW-Authenticate",
972                           apr_psprintf(req->pool, "Basic realm=\"%s\"",
973                                        ap_auth_name(req)));
974         }
975     }
976     gss_release_oid_set(&min, &indicated_mechs);
977     if (ctx != GSS_C_NO_CONTEXT)
978         gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
979     gss_release_cred(&min, &acquired_cred);
980     gss_release_cred(&min, &delegated_cred);
981     gss_release_buffer(&min, &output);
982     gss_release_name(&min, &client);
983     gss_release_buffer(&min, &name);
984     gss_release_buffer(&min, &lname);
985     return ret;
986 }
987
988
989 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
990 {
991     struct mag_config *cfg;
992
993     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
994     cfg->pool = p;
995
996     return cfg;
997 }
998
999 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
1000 {
1001     struct mag_config *cfg = (struct mag_config *)mconfig;
1002     cfg->ssl_only = on ? true : false;
1003     return NULL;
1004 }
1005
1006 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
1007 {
1008     struct mag_config *cfg = (struct mag_config *)mconfig;
1009     cfg->map_to_local = on ? true : false;
1010     return NULL;
1011 }
1012
1013 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
1014 {
1015     struct mag_config *cfg = (struct mag_config *)mconfig;
1016     cfg->gss_conn_ctx = on ? true : false;
1017     return NULL;
1018 }
1019
1020 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
1021 {
1022     struct mag_config *cfg = (struct mag_config *)mconfig;
1023     cfg->send_persist = on ? true : false;
1024     return NULL;
1025 }
1026
1027 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
1028 {
1029     struct mag_config *cfg = (struct mag_config *)mconfig;
1030     cfg->use_sessions = on ? true : false;
1031     return NULL;
1032 }
1033
1034 #ifdef HAVE_CRED_STORE
1035 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
1036 {
1037     struct mag_config *cfg = (struct mag_config *)mconfig;
1038     cfg->use_s4u2proxy = on ? true : false;
1039
1040     if (cfg->deleg_ccache_dir == NULL) {
1041         cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
1042     }
1043     return NULL;
1044 }
1045 #endif
1046
1047 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
1048 {
1049     struct mag_config *cfg = (struct mag_config *)mconfig;
1050     struct databuf keys;
1051     unsigned char *val;
1052     apr_status_t rc;
1053     const char *k;
1054     int l;
1055
1056     if (strncmp(w, "key:", 4) != 0) {
1057         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1058                      "Invalid key format, expected prefix 'key:'");
1059         return NULL;
1060     }
1061     k = w + 4;
1062
1063     l = apr_base64_decode_len(k);
1064     val = apr_palloc(parms->temp_pool, l);
1065
1066     keys.length = (int)apr_base64_decode_binary(val, k);
1067     keys.value = (unsigned char *)val;
1068
1069     if (keys.length != 32) {
1070         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1071                      "Invalid key length, expected 32 got %d", keys.length);
1072         return NULL;
1073     }
1074
1075     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
1076     if (rc != OK) {
1077         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1078                      "Failed to import sealing key!");
1079     }
1080     return NULL;
1081 }
1082
1083 #ifdef HAVE_CRED_STORE
1084
1085 #define MAX_CRED_OPTIONS 10
1086
1087 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
1088                                   const char *w)
1089 {
1090     struct mag_config *cfg = (struct mag_config *)mconfig;
1091     gss_key_value_element_desc *elements;
1092     uint32_t count;
1093     size_t size;
1094     const char *p;
1095     char *value;
1096     char *key;
1097
1098     p = strchr(w, ':');
1099     if (!p) {
1100         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1101                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
1102         return NULL;
1103     }
1104
1105     key = apr_pstrndup(parms->pool, w, (p-w));
1106     value = apr_pstrdup(parms->pool, p + 1);
1107
1108     if (!cfg->cred_store) {
1109         cfg->cred_store = apr_pcalloc(parms->pool,
1110                                       sizeof(gss_key_value_set_desc));
1111         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
1112         cfg->cred_store->elements = apr_palloc(parms->pool, size);
1113     }
1114
1115     elements = cfg->cred_store->elements;
1116     count = cfg->cred_store->count;
1117
1118     if (count >= MAX_CRED_OPTIONS) {
1119         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1120                      "Too many GssapiCredStore options (MAX: %d)",
1121                      MAX_CRED_OPTIONS);
1122         return NULL;
1123     }
1124     cfg->cred_store->count++;
1125
1126     elements[count].key = key;
1127     elements[count].value = value;
1128
1129     return NULL;
1130 }
1131
1132 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
1133                                         const char *value)
1134 {
1135     struct mag_config *cfg = (struct mag_config *)mconfig;
1136
1137     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
1138
1139     return NULL;
1140 }
1141 #endif
1142
1143 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1144 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
1145 {
1146     struct mag_config *cfg = (struct mag_config *)mconfig;
1147
1148     cfg->use_basic_auth = on ? true : false;
1149     return NULL;
1150 }
1151 #endif
1152
1153 static apr_status_t mag_oid_set_destroy(void *ptr)
1154 {
1155     uint32_t min;
1156     gss_OID_set set = (gss_OID_set)ptr;
1157     (void)gss_release_oid_set(&min, &set);
1158     return APR_SUCCESS;
1159 }
1160
1161 static void mag_list_of_mechs(cmd_parms *parms, gss_OID_set *oidset,
1162                               bool add_spnego, const char *w)
1163 {
1164     gss_buffer_desc buf = { 0 };
1165     uint32_t maj, min;
1166     gss_OID_set set;
1167     gss_OID oid;
1168     bool release_oid = false;
1169
1170     if (NULL == *oidset) {
1171         maj = gss_create_empty_oid_set(&min, &set);
1172         if (maj != GSS_S_COMPLETE) {
1173             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1174                          "gss_create_empty_oid_set() failed.");
1175             *oidset = GSS_C_NO_OID_SET;
1176             return;
1177         }
1178         if (add_spnego) {
1179             oid = discard_const(&gss_mech_spnego);
1180             maj = gss_add_oid_set_member(&min, oid, &set);
1181             if (maj != GSS_S_COMPLETE) {
1182                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1183                              "gss_add_oid_set_member() failed.");
1184                 (void)gss_release_oid_set(&min, &set);
1185                 *oidset = GSS_C_NO_OID_SET;
1186                 return;
1187             }
1188         }
1189         /* register in the pool so it can be released once the server
1190          * winds down */
1191         apr_pool_cleanup_register(parms->pool, (void *)set,
1192                                   mag_oid_set_destroy,
1193                                   apr_pool_cleanup_null);
1194         *oidset = set;
1195     } else {
1196         set = *oidset;
1197     }
1198
1199     if (strcmp(w, "krb5") == 0) {
1200         oid = discard_const(gss_mech_krb5);
1201     } else if (strcmp(w, "iakerb") == 0) {
1202         oid = discard_const(gss_mech_iakerb);
1203     } else if (strcmp(w, "ntlmssp") == 0) {
1204         oid = discard_const(&gss_mech_ntlmssp);
1205     } else {
1206         buf.value = discard_const(w);
1207         buf.length = strlen(w);
1208         maj = gss_str_to_oid(&min, &buf, &oid);
1209         if (maj != GSS_S_COMPLETE) {
1210             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1211                          "Unrecognized GSSAPI Mechanism: [%s]", w);
1212             return;
1213         }
1214         release_oid = true;
1215     }
1216     maj = gss_add_oid_set_member(&min, oid, &set);
1217     if (maj != GSS_S_COMPLETE) {
1218         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1219                          "gss_add_oid_set_member() failed for [%s].", w);
1220     }
1221     if (release_oid) {
1222         (void)gss_release_oid(&min, &oid);
1223     }
1224 }
1225
1226 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
1227                                   const char *w)
1228 {
1229     struct mag_config *cfg = (struct mag_config *)mconfig;
1230
1231     mag_list_of_mechs(parms, &cfg->allowed_mechs, true, w);
1232
1233     return NULL;
1234 }
1235
1236 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1237 static const char *mag_basic_auth_mechs(cmd_parms *parms, void *mconfig,
1238                                         const char *w)
1239 {
1240     struct mag_config *cfg = (struct mag_config *)mconfig;
1241
1242     mag_list_of_mechs(parms, &cfg->basic_mechs, false, w);
1243
1244     return NULL;
1245 }
1246 #endif
1247
1248 static const command_rec mag_commands[] = {
1249     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
1250                   "Work only if connection is SSL Secured"),
1251     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
1252                   "Translate principals to local names"),
1253     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
1254                   "Authentication is bound to the TCP connection"),
1255     AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
1256                   "Send Persitent-Auth header according to connection bound"),
1257     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
1258                   "Authentication uses mod_sessions to hold status"),
1259     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
1260                      "Key Used to seal session data."),
1261 #ifdef HAVE_CRED_STORE
1262     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
1263                   "Initializes credentials for s4u2proxy usage"),
1264     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
1265                     "Credential Store"),
1266     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
1267                      OR_AUTHCFG, "Directory to store delegated credentials"),
1268 #endif
1269 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1270     AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
1271                      "Allows use of Basic Auth for authentication"),
1272     AP_INIT_ITERATE("GssapiBasicAuthMech", mag_basic_auth_mechs, NULL,
1273                     OR_AUTHCFG, "Mechanisms to use for basic auth"),
1274 #endif
1275     AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
1276                     "Allowed Mechanisms"),
1277     { NULL }
1278 };
1279
1280 static void
1281 mag_register_hooks(apr_pool_t *p)
1282 {
1283     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1284     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1285     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1286 }
1287
1288 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1289 {
1290     STANDARD20_MODULE_STUFF,
1291     mag_create_dir_config,
1292     NULL,
1293     NULL,
1294     NULL,
1295     mag_commands,
1296     mag_register_hooks
1297 };