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