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