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