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