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