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