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