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