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