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