Properly check return error when filtering mechs
[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     if (src == GSS_C_NO_OID_SET) return GSS_C_NO_OID_SET;
353
354     for (int i = 0; unwanted_mechs[i] != GSS_C_NO_OID; i++) {
355         maj = gss_test_oid_set_member(&min,
356                                       discard_const(unwanted_mechs[i]),
357                                       src, &present);
358         if (present) break;
359     }
360     if (present) {
361         maj = gss_create_empty_oid_set(&min, &dst);
362         if (maj != GSS_S_COMPLETE) {
363             return GSS_C_NO_OID_SET;
364         }
365         for (int i = 0; i < src->count; i++) {
366             present = 0;
367             for (int j = 0; unwanted_mechs[j] != GSS_C_NO_OID; j++) {
368                 if (gss_oid_equal(&src->elements[i], unwanted_mechs[j])) {
369                     present = 1;
370                     break;
371                 }
372             }
373             if (present) continue;
374             maj = gss_add_oid_set_member(&min, &src->elements[i], &dst);
375             if (maj != GSS_S_COMPLETE) {
376                 gss_release_oid_set(&min, &dst);
377                 return GSS_C_NO_OID_SET;
378             }
379         }
380         return dst;
381     }
382     return src;
383 }
384
385 static bool mag_auth_basic(request_rec *req,
386                            struct mag_config *cfg,
387                            gss_buffer_desc ba_user,
388                            gss_buffer_desc ba_pwd,
389                            gss_cred_usage_t cred_usage,
390                            gss_name_t *client,
391                            gss_OID *mech_type,
392                            gss_cred_id_t *delegated_cred,
393                            uint32_t *vtime)
394 {
395 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
396     const char *user_ccache = NULL;
397     const char *orig_ccache = NULL;
398     long long unsigned int rndname;
399     apr_status_t rs;
400 #endif
401     gss_name_t user = GSS_C_NO_NAME;
402     gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
403     gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
404     gss_name_t server = GSS_C_NO_NAME;
405     gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
406     gss_ctx_id_t server_ctx = GSS_C_NO_CONTEXT;
407     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
408     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
409     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
410     gss_OID_set indicated_mechs = GSS_C_NO_OID_SET;
411     gss_OID_set allowed_mechs;
412     gss_OID_set filtered_mechs;
413     gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
414     uint32_t init_flags = 0;
415     uint32_t maj, min;
416     int present = 0;
417     bool ret = false;
418
419     maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &user);
420     if (GSS_ERROR(maj)) {
421         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
422                       "In Basic Auth, %s",
423                       mag_error(req, "gss_import_name() failed",
424                                 maj, min));
425         goto done;
426     }
427
428     if (cfg->basic_mechs) {
429         allowed_mechs = cfg->basic_mechs;
430     } else if (cfg->allowed_mechs) {
431         allowed_mechs = cfg->allowed_mechs;
432     } else {
433         /* Try to fetch the default set if not explicitly configured,
434          * We need to do this because gss_acquire_cred_with_password()
435          * is currently limited to acquire creds for a single "default"
436          * mechanism if no desired mechanisms are passed in. This causes
437          * authentication to fail for secondary mechanisms as no user
438          * credentials are generated for those. */
439         maj = gss_indicate_mechs(&min, &indicated_mechs);
440         if (maj != GSS_S_COMPLETE) {
441             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "%s",
442                           mag_error(req, "gss_indicate_mechs() failed",
443                                     maj, min));
444             /* if indicated _mechs failed, set GSS_C_NO_OID_SET. This
445              * generally causes only the krb5 mechanism to be tried due
446              * to implementation constraints, but may change in future. */
447             allowed_mechs = GSS_C_NO_OID_SET;
448         } else {
449             allowed_mechs = indicated_mechs;
450         }
451     }
452
453     /* Remove Spnego if present, or we'd repeat failed authentiations
454      * multiple times, one within Spnego and then again with an explicit
455      * mechanism. We would normally just force Spnego and use
456      * gss_set_neg_mechs, but due to the way we source the server name
457      * and the fact MIT up to 1.14 at least does no handle union names,
458      * we can't provide spnego with a server name that can be used by
459      * multiple mechanisms, causing any but the first mechanism to fail.
460      * Also remove unwanted krb mechs, or AS requests will be repeated
461      * multiple times uselessly.
462      */
463     filtered_mechs = mag_filter_unwanted_mechs(allowed_mechs);
464     if ((allowed_mechs != GSS_C_NO_OID_SET) &&
465         (filtered_mechs == GSS_C_NO_OID_SET)) {
466         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "Fatal "
467                       "failure while filtering mechs, aborting");
468         goto done;
469     } else if (filtered_mechs != allowed_mechs) {
470         /* if indicated_mechs where sourced then free them here before
471          * reusing the pointer */
472         gss_release_oid_set(&min, &indicated_mechs);
473
474         /* mark the list of mechs needs to be freed */
475         indicated_mechs = filtered_mechs;
476
477         /* use the filtered list */
478         allowed_mechs = filtered_mechs;
479     }
480
481 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
482     /* If we are using the krb5 mechanism make sure to set a per thread
483      * memory ccache so that there can't be interferences between threads.
484      * Also make sure we have  new cache so no cached results end up being
485      * used. Some implementations of gss_acquire_cred_with_password() do
486      * not reacquire creds if cached ones are around, failing to check
487      * again for the password. */
488     maj = gss_test_oid_set_member(&min, discard_const(gss_mech_krb5),
489                                   allowed_mechs, &present);
490     if (GSS_ERROR(maj)) {
491         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
492                       "In Basic Auth, %s",
493                       mag_error(req, "gss_test_oid_set_member() failed",
494                                 maj, min));
495         goto done;
496     }
497     if (present) {
498         rs = apr_generate_random_bytes((unsigned char *)(&rndname),
499                                        sizeof(long long unsigned int));
500         if (rs != APR_SUCCESS) {
501             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
502                           "Failed to generate random ccache name");
503             goto done;
504         }
505         user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
506         maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
507         if (GSS_ERROR(maj)) {
508             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
509                           "In Basic Auth, %s",
510                           mag_error(req, "gss_krb5_ccache_name() "
511                                     "failed", maj, min));
512             goto done;
513         }
514     }
515 #endif
516
517     maj = gss_acquire_cred_with_password(&min, user, &ba_pwd,
518                                          GSS_C_INDEFINITE,
519                                          allowed_mechs,
520                                          GSS_C_INITIATE,
521                                          &user_cred, &actual_mechs, NULL);
522     if (GSS_ERROR(maj)) {
523         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
524                       "In Basic Auth, %s",
525                       mag_error(req, "gss_acquire_cred_with_password() "
526                                 "failed", maj, min));
527         goto done;
528     }
529
530     /* must acquire creds based on the actual mechs we want to try */
531     if (!mag_acquire_creds(req, cfg, actual_mechs,
532                            cred_usage, &acquired_cred, NULL)) {
533         goto done;
534     }
535
536     if (cred_usage == GSS_C_BOTH) {
537         /* must acquire with GSS_C_ACCEPT to get the server name */
538         if (!mag_acquire_creds(req, cfg, actual_mechs,
539                                GSS_C_ACCEPT, &server_cred, NULL)) {
540             goto done;
541         }
542     } else {
543         server_cred = acquired_cred;
544     }
545
546 #ifdef HAVE_CRED_STORE
547     if (cfg->deleg_ccache_dir) {
548         /* delegate ourselves credentials so we store them as requested */
549         init_flags |= GSS_C_DELEG_FLAG;
550     }
551 #endif
552
553     for (int i = 0; i < actual_mechs->count; i++) {
554
555         /* free these if looping */
556         gss_release_buffer(&min, &output);
557         gss_release_buffer(&min, &input);
558         gss_release_name(&min, &server);
559
560         maj = gss_inquire_cred_by_mech(&min, server_cred,
561                                        &actual_mechs->elements[i],
562                                        &server, NULL, NULL, NULL);
563         if (GSS_ERROR(maj)) {
564             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
565                           "%s", mag_error(req, "gss_inquired_cred_by_mech() "
566                                           "failed", maj, min));
567             continue;
568         }
569
570         do {
571             /* output and input are inverted here, this is intentional */
572             maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
573                                        &actual_mechs->elements[i], init_flags,
574                                        300, GSS_C_NO_CHANNEL_BINDINGS, &output,
575                                        NULL, &input, NULL, NULL);
576             if (GSS_ERROR(maj)) {
577                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
578                               "%s", mag_error(req, "gss_init_sec_context() "
579                                               "failed", maj, min));
580                 break;
581             }
582             gss_release_buffer(&min, &output);
583             maj = gss_accept_sec_context(&min, &server_ctx, acquired_cred,
584                                          &input, GSS_C_NO_CHANNEL_BINDINGS,
585                                          client, mech_type, &output, NULL,
586                                          vtime, delegated_cred);
587             if (GSS_ERROR(maj)) {
588                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
589                               "%s", mag_error(req, "gss_accept_sec_context()"
590                                               " failed", maj, min));
591                 break;
592             }
593             gss_release_buffer(&min, &input);
594         } while (maj == GSS_S_CONTINUE_NEEDED);
595
596         if (maj == GSS_S_COMPLETE) {
597             ret = true;
598             break;
599         }
600     }
601
602 done:
603     gss_release_buffer(&min, &output);
604     gss_release_buffer(&min, &input);
605     gss_release_name(&min, &server);
606     if (server_cred != acquired_cred)
607         gss_release_cred(&min, &server_cred);
608     gss_delete_sec_context(&min, &server_ctx, GSS_C_NO_BUFFER);
609     gss_release_cred(&min, &acquired_cred);
610     gss_release_name(&min, &user);
611     gss_release_cred(&min, &user_cred);
612     gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER);
613     gss_release_oid_set(&min, &actual_mechs);
614     gss_release_oid_set(&min, &indicated_mechs);
615 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
616     if (user_ccache != NULL) {
617         maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
618         if (maj != GSS_S_COMPLETE) {
619             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
620                           "Failed to restore per-thread ccache, %s",
621                           mag_error(req, "gss_krb5_ccache_name() "
622                                     "failed", maj, min));
623         }
624     }
625 #endif
626     return ret;
627 }
628
629
630 static int mag_auth(request_rec *req)
631 {
632     const char *type;
633     int auth_type = -1;
634     struct mag_config *cfg;
635     const char *auth_header;
636     char *auth_header_type;
637     int ret = HTTP_UNAUTHORIZED;
638     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
639     gss_ctx_id_t *pctx;
640     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
641     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
642     gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
643     gss_buffer_desc ba_user;
644     gss_buffer_desc ba_pwd;
645     gss_name_t client = GSS_C_NO_NAME;
646     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
647     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
648     gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
649     uint32_t vtime;
650     uint32_t maj, min;
651     char *reply;
652     size_t replen;
653     char *clientname;
654     gss_OID mech_type = GSS_C_NO_OID;
655     gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
656     gss_OID_set indicated_mechs = GSS_C_NO_OID_SET;
657     gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
658     struct mag_conn *mc = NULL;
659     time_t expiration;
660     int i;
661
662     type = ap_auth_type(req);
663     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
664         return DECLINED;
665     }
666
667     cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
668
669     if (cfg->allowed_mechs) {
670         desired_mechs = cfg->allowed_mechs;
671     } else {
672         /* Try to fetch the default set if not explicitly configured */
673         maj = gss_indicate_mechs(&min, &indicated_mechs);
674         if (maj != GSS_S_COMPLETE) {
675             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "%s",
676                           mag_error(req, "gss_indicate_mechs() failed",
677                                     maj, min));
678         }
679         desired_mechs = indicated_mechs;
680     }
681
682     /* implicit auth for subrequests if main auth already happened */
683     if (!ap_is_initial_req(req) && req->main != NULL) {
684         type = ap_auth_type(req->main);
685         if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
686             /* warn if the subrequest location and the main request
687              * location have different configs */
688             if (cfg != ap_get_module_config(req->main->per_dir_config,
689                                             &auth_gssapi_module)) {
690                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
691                               req, "Subrequest authentication bypass on "
692                                    "location with different configuration!");
693             }
694             if (req->main->user) {
695                 req->user = apr_pstrdup(req->pool, req->main->user);
696                 return OK;
697             } else {
698                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
699                               "The main request is tasked to establish the "
700                               "security context, can't proceed!");
701                 return HTTP_UNAUTHORIZED;
702             }
703         } else {
704             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
705                           "Subrequest GSSAPI auth with no auth on the main "
706                           "request. This operation may fail if other "
707                           "subrequests already established a context or the "
708                           "mechanism requires multiple roundtrips.");
709         }
710     }
711
712     if (cfg->ssl_only) {
713         if (!mag_conn_is_https(req->connection)) {
714             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
715                           "Not a TLS connection, refusing to authenticate!");
716             goto done;
717         }
718     }
719
720     if (cfg->gss_conn_ctx) {
721         mc = (struct mag_conn *)ap_get_module_config(
722                                                 req->connection->conn_config,
723                                                 &auth_gssapi_module);
724         if (!mc) {
725             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
726                           "Failed to retrieve connection context!");
727             goto done;
728         }
729     }
730
731     /* if available, session always supersedes connection bound data */
732     if (cfg->use_sessions) {
733         mag_check_session(req, cfg, &mc);
734     }
735
736     auth_header = apr_table_get(req->headers_in, "Authorization");
737
738     if (mc) {
739         if (mc->established &&
740             (auth_header == NULL) &&
741             (mc->auth_type != AUTH_TYPE_BASIC)) {
742             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
743                           "Already established context found!");
744             mag_set_req_data(req, cfg, mc);
745             ret = OK;
746             goto done;
747         }
748         pctx = &mc->ctx;
749     } else {
750         pctx = &ctx;
751     }
752
753     /* We can proceed only if we do have an auth header */
754     if (!auth_header) goto done;
755
756     auth_header_type = ap_getword_white(req->pool, &auth_header);
757     if (!auth_header_type) goto done;
758
759     for (i = 0; auth_types[i] != NULL; i++) {
760         if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
761             auth_type = i;
762             break;
763         }
764     }
765
766     switch (auth_type) {
767     case AUTH_TYPE_NEGOTIATE:
768         if (!parse_auth_header(req->pool, &auth_header, &input)) {
769             goto done;
770         }
771         break;
772     case AUTH_TYPE_BASIC:
773         if (!cfg->use_basic_auth) {
774             goto done;
775         }
776
777         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
778         if (!ba_pwd.value) goto done;
779         ba_user.value = ap_getword_nulls_nc(req->pool,
780                                             (char **)&ba_pwd.value, ':');
781         if (!ba_user.value) goto done;
782         if (((char *)ba_user.value)[0] == '\0' ||
783             ((char *)ba_pwd.value)[0] == '\0') {
784             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
785                           "Invalid empty user or password for Basic Auth");
786             goto done;
787         }
788         ba_user.length = strlen(ba_user.value);
789         ba_pwd.length = strlen(ba_pwd.value);
790
791         if (mc && mc->established &&
792             mag_basic_check(cfg, mc, ba_user, ba_pwd)) {
793             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
794                           "Already established BASIC AUTH context found!");
795             mag_set_req_data(req, cfg, mc);
796             ret = OK;
797             goto done;
798         }
799
800         break;
801
802     case AUTH_TYPE_RAW_NTLM:
803         if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
804             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
805                           "NTLM Authentication is not allowed!");
806             goto done;
807         }
808
809         if (!parse_auth_header(req->pool, &auth_header, &input)) {
810             goto done;
811         }
812
813         desired_mechs = discard_const(&gss_mech_set_ntlmssp);
814         break;
815
816     default:
817         goto done;
818     }
819
820     if (mc && mc->established) {
821         /* if we are re-authenticating make sure the conn context
822          * is cleaned up so we do not accidentally reuse an existing
823          * established context */
824         mag_conn_clear(mc);
825     }
826
827     req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
828
829 #ifdef HAVE_CRED_STORE
830     if (cfg->use_s4u2proxy) {
831         cred_usage = GSS_C_BOTH;
832     }
833 #endif
834
835     if (auth_type == AUTH_TYPE_BASIC) {
836         if (mag_auth_basic(req, cfg, ba_user, ba_pwd,
837                            cred_usage, &client, &mech_type,
838                            &delegated_cred, &vtime)) {
839             goto complete;
840         }
841         goto done;
842     }
843
844     if (!mag_acquire_creds(req, cfg, desired_mechs,
845                            cred_usage, &acquired_cred, NULL)) {
846         goto done;
847     }
848
849     if (auth_type == AUTH_TYPE_NEGOTIATE &&
850         cfg->allowed_mechs != GSS_C_NO_OID_SET) {
851         maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
852         if (GSS_ERROR(maj)) {
853             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
854                           mag_error(req, "gss_set_neg_mechs() failed",
855                                     maj, min));
856             goto done;
857         }
858     }
859
860     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
861                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
862                                  &client, &mech_type, &output, NULL, &vtime,
863                                  &delegated_cred);
864     if (GSS_ERROR(maj)) {
865         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
866                       mag_error(req, "gss_accept_sec_context() failed",
867                                 maj, min));
868         goto done;
869     } else if (maj == GSS_S_CONTINUE_NEEDED) {
870         if (!mc) {
871             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
872                           "Mechanism needs continuation but neither "
873                           "GssapiConnectionBound nor "
874                           "GssapiUseSessions are available");
875             gss_release_buffer(&min, &output);
876             output.length = 0;
877         }
878         /* auth not complete send token and wait next packet */
879         goto done;
880     }
881
882 complete:
883     /* Always set the GSS name in an env var */
884     maj = gss_display_name(&min, client, &name, NULL);
885     if (GSS_ERROR(maj)) {
886         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
887                       mag_error(req, "gss_display_name() failed",
888                                 maj, min));
889         goto done;
890     }
891     clientname = apr_pstrndup(req->pool, name.value, name.length);
892     apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
893     expiration = time(NULL) + vtime;
894     apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
895                   apr_psprintf(req->pool, "%ld", (long)expiration));
896
897 #ifdef HAVE_CRED_STORE
898     if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
899         char *ccachefile = NULL;
900
901         mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
902                               delegated_cred, &ccachefile);
903
904         if (ccachefile) {
905             mag_set_KRB5CCANME(req, ccachefile);
906         }
907
908         if (mc) {
909             mc->delegated = true;
910         }
911     }
912 #endif
913
914     if (cfg->map_to_local) {
915         maj = gss_localname(&min, client, mech_type, &lname);
916         if (maj != GSS_S_COMPLETE) {
917             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
918                           mag_error(req, "gss_localname() failed", maj, min));
919             goto done;
920         }
921         req->user = apr_pstrndup(req->pool, lname.value, lname.length);
922     } else {
923         req->user = clientname;
924     }
925
926     if (mc) {
927         mc->user_name = apr_pstrdup(mc->pool, req->user);
928         mc->gss_name = apr_pstrdup(mc->pool, clientname);
929         mc->established = true;
930         if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
931             vtime = MIN_SESS_EXP_TIME;
932         }
933         mc->expiration = expiration;
934         mc->auth_type = auth_type;
935         if (auth_type == AUTH_TYPE_BASIC) {
936             mag_basic_cache(cfg, mc, ba_user, ba_pwd);
937         }
938         if (cfg->use_sessions) {
939             mag_attempt_session(req, cfg, mc);
940         }
941     }
942
943     if (cfg->send_persist)
944         apr_table_set(req->headers_out, "Persistent-Auth",
945             cfg->gss_conn_ctx ? "true" : "false");
946
947     ret = OK;
948
949 done:
950     if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
951         int prefixlen = strlen(auth_types[auth_type]) + 1;
952         replen = apr_base64_encode_len(output.length) + 1;
953         reply = apr_pcalloc(req->pool, prefixlen + replen);
954         if (reply) {
955             memcpy(reply, auth_types[auth_type], prefixlen - 1);
956             reply[prefixlen - 1] = ' ';
957             apr_base64_encode(&reply[prefixlen], output.value, output.length);
958             apr_table_add(req->err_headers_out,
959                           "WWW-Authenticate", reply);
960         }
961     } else if (ret == HTTP_UNAUTHORIZED) {
962         apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
963         if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
964             apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
965         }
966         if (cfg->use_basic_auth) {
967             apr_table_add(req->err_headers_out,
968                           "WWW-Authenticate",
969                           apr_psprintf(req->pool, "Basic realm=\"%s\"",
970                                        ap_auth_name(req)));
971         }
972     }
973     gss_release_oid_set(&min, &indicated_mechs);
974     if (ctx != GSS_C_NO_CONTEXT)
975         gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
976     gss_release_cred(&min, &acquired_cred);
977     gss_release_cred(&min, &delegated_cred);
978     gss_release_buffer(&min, &output);
979     gss_release_name(&min, &client);
980     gss_release_buffer(&min, &name);
981     gss_release_buffer(&min, &lname);
982     return ret;
983 }
984
985
986 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
987 {
988     struct mag_config *cfg;
989
990     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
991     cfg->pool = p;
992
993     return cfg;
994 }
995
996 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
997 {
998     struct mag_config *cfg = (struct mag_config *)mconfig;
999     cfg->ssl_only = on ? true : false;
1000     return NULL;
1001 }
1002
1003 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
1004 {
1005     struct mag_config *cfg = (struct mag_config *)mconfig;
1006     cfg->map_to_local = on ? true : false;
1007     return NULL;
1008 }
1009
1010 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
1011 {
1012     struct mag_config *cfg = (struct mag_config *)mconfig;
1013     cfg->gss_conn_ctx = on ? true : false;
1014     return NULL;
1015 }
1016
1017 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
1018 {
1019     struct mag_config *cfg = (struct mag_config *)mconfig;
1020     cfg->send_persist = on ? true : false;
1021     return NULL;
1022 }
1023
1024 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
1025 {
1026     struct mag_config *cfg = (struct mag_config *)mconfig;
1027     cfg->use_sessions = on ? true : false;
1028     return NULL;
1029 }
1030
1031 #ifdef HAVE_CRED_STORE
1032 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
1033 {
1034     struct mag_config *cfg = (struct mag_config *)mconfig;
1035     cfg->use_s4u2proxy = on ? true : false;
1036
1037     if (cfg->deleg_ccache_dir == NULL) {
1038         cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
1039     }
1040     return NULL;
1041 }
1042 #endif
1043
1044 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
1045 {
1046     struct mag_config *cfg = (struct mag_config *)mconfig;
1047     struct databuf keys;
1048     unsigned char *val;
1049     apr_status_t rc;
1050     const char *k;
1051     int l;
1052
1053     if (strncmp(w, "key:", 4) != 0) {
1054         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1055                      "Invalid key format, expected prefix 'key:'");
1056         return NULL;
1057     }
1058     k = w + 4;
1059
1060     l = apr_base64_decode_len(k);
1061     val = apr_palloc(parms->temp_pool, l);
1062
1063     keys.length = (int)apr_base64_decode_binary(val, k);
1064     keys.value = (unsigned char *)val;
1065
1066     if (keys.length != 32) {
1067         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1068                      "Invalid key length, expected 32 got %d", keys.length);
1069         return NULL;
1070     }
1071
1072     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
1073     if (rc != OK) {
1074         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1075                      "Failed to import sealing key!");
1076     }
1077     return NULL;
1078 }
1079
1080 #ifdef HAVE_CRED_STORE
1081
1082 #define MAX_CRED_OPTIONS 10
1083
1084 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
1085                                   const char *w)
1086 {
1087     struct mag_config *cfg = (struct mag_config *)mconfig;
1088     gss_key_value_element_desc *elements;
1089     uint32_t count;
1090     size_t size;
1091     const char *p;
1092     char *value;
1093     char *key;
1094
1095     p = strchr(w, ':');
1096     if (!p) {
1097         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1098                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
1099         return NULL;
1100     }
1101
1102     key = apr_pstrndup(parms->pool, w, (p-w));
1103     value = apr_pstrdup(parms->pool, p + 1);
1104
1105     if (!cfg->cred_store) {
1106         cfg->cred_store = apr_pcalloc(parms->pool,
1107                                       sizeof(gss_key_value_set_desc));
1108         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
1109         cfg->cred_store->elements = apr_palloc(parms->pool, size);
1110     }
1111
1112     elements = cfg->cred_store->elements;
1113     count = cfg->cred_store->count;
1114
1115     if (count >= MAX_CRED_OPTIONS) {
1116         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1117                      "Too many GssapiCredStore options (MAX: %d)",
1118                      MAX_CRED_OPTIONS);
1119         return NULL;
1120     }
1121     cfg->cred_store->count++;
1122
1123     elements[count].key = key;
1124     elements[count].value = value;
1125
1126     return NULL;
1127 }
1128
1129 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
1130                                         const char *value)
1131 {
1132     struct mag_config *cfg = (struct mag_config *)mconfig;
1133
1134     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
1135
1136     return NULL;
1137 }
1138 #endif
1139
1140 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1141 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
1142 {
1143     struct mag_config *cfg = (struct mag_config *)mconfig;
1144
1145     cfg->use_basic_auth = on ? true : false;
1146     return NULL;
1147 }
1148 #endif
1149
1150 static apr_status_t mag_oid_set_destroy(void *ptr)
1151 {
1152     uint32_t min;
1153     gss_OID_set set = (gss_OID_set)ptr;
1154     (void)gss_release_oid_set(&min, &set);
1155     return APR_SUCCESS;
1156 }
1157
1158 static bool mag_list_of_mechs(cmd_parms *parms, gss_OID_set *oidset,
1159                               bool add_spnego, const char *w)
1160 {
1161     gss_buffer_desc buf = { 0 };
1162     uint32_t maj, min;
1163     gss_OID_set set;
1164     gss_OID oid;
1165     bool release_oid = false;
1166
1167     if (NULL == *oidset) {
1168         maj = gss_create_empty_oid_set(&min, &set);
1169         if (maj != GSS_S_COMPLETE) {
1170             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1171                          "gss_create_empty_oid_set() failed.");
1172             *oidset = GSS_C_NO_OID_SET;
1173             return false;
1174         }
1175         if (add_spnego) {
1176             oid = discard_const(&gss_mech_spnego);
1177             maj = gss_add_oid_set_member(&min, oid, &set);
1178             if (maj != GSS_S_COMPLETE) {
1179                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1180                              "gss_add_oid_set_member() failed.");
1181                 (void)gss_release_oid_set(&min, &set);
1182                 *oidset = GSS_C_NO_OID_SET;
1183                 return false;
1184             }
1185         }
1186         /* register in the pool so it can be released once the server
1187          * winds down */
1188         apr_pool_cleanup_register(parms->pool, (void *)set,
1189                                   mag_oid_set_destroy,
1190                                   apr_pool_cleanup_null);
1191         *oidset = set;
1192     } else {
1193         set = *oidset;
1194     }
1195
1196     if (strcmp(w, "krb5") == 0) {
1197         oid = discard_const(gss_mech_krb5);
1198     } else if (strcmp(w, "iakerb") == 0) {
1199         oid = discard_const(gss_mech_iakerb);
1200     } else if (strcmp(w, "ntlmssp") == 0) {
1201         oid = discard_const(&gss_mech_ntlmssp);
1202     } else {
1203         buf.value = discard_const(w);
1204         buf.length = strlen(w);
1205         maj = gss_str_to_oid(&min, &buf, &oid);
1206         if (maj != GSS_S_COMPLETE) {
1207             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1208                          "Unrecognized GSSAPI Mechanism: [%s]", w);
1209             return false;
1210         }
1211         release_oid = true;
1212     }
1213     maj = gss_add_oid_set_member(&min, oid, &set);
1214     if (maj != GSS_S_COMPLETE) {
1215         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1216                          "gss_add_oid_set_member() failed for [%s].", w);
1217     }
1218     if (release_oid) {
1219         (void)gss_release_oid(&min, &oid);
1220     }
1221
1222     return true;
1223 }
1224
1225 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
1226                                   const char *w)
1227 {
1228     struct mag_config *cfg = (struct mag_config *)mconfig;
1229
1230     if (!mag_list_of_mechs(parms, &cfg->allowed_mechs, true, w))
1231         return "Failed to apply GssapiAllowedMech directive";
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     if (!mag_list_of_mechs(parms, &cfg->basic_mechs, false, w))
1243         return "Failed to apply GssapiBasicAuthMech directive";
1244
1245     return NULL;
1246 }
1247 #endif
1248
1249 static const command_rec mag_commands[] = {
1250     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
1251                   "Work only if connection is SSL Secured"),
1252     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
1253                   "Translate principals to local names"),
1254     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
1255                   "Authentication is bound to the TCP connection"),
1256     AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
1257                   "Send Persitent-Auth header according to connection bound"),
1258     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
1259                   "Authentication uses mod_sessions to hold status"),
1260     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
1261                      "Key Used to seal session data."),
1262 #ifdef HAVE_CRED_STORE
1263     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
1264                   "Initializes credentials for s4u2proxy usage"),
1265     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
1266                     "Credential Store"),
1267     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
1268                      OR_AUTHCFG, "Directory to store delegated credentials"),
1269 #endif
1270 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1271     AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
1272                      "Allows use of Basic Auth for authentication"),
1273     AP_INIT_ITERATE("GssapiBasicAuthMech", mag_basic_auth_mechs, NULL,
1274                     OR_AUTHCFG, "Mechanisms to use for basic auth"),
1275 #endif
1276     AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
1277                     "Allowed Mechanisms"),
1278     { NULL }
1279 };
1280
1281 static void
1282 mag_register_hooks(apr_pool_t *p)
1283 {
1284     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1285     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1286     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1287 }
1288
1289 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1290 {
1291     STANDARD20_MODULE_STUFF,
1292     mag_create_dir_config,
1293     NULL,
1294     NULL,
1295     NULL,
1296     mag_commands,
1297     mag_register_hooks
1298 };