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