4 Copyright (C) 2014 Simo Sorce <simo@redhat.com>
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:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
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.
25 #include "mod_auth_gssapi.h"
27 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
29 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
31 APLOG_USE_MODULE(auth_gssapi);
33 static char *mag_status(request_rec *req, int type, uint32_t err)
35 uint32_t maj_ret, min_ret;
44 maj_ret = gss_display_status(&min_ret, err, type,
45 GSS_C_NO_OID, &msg_ctx, &text);
46 if (maj_ret != GSS_S_COMPLETE) {
52 msg_ret = apr_psprintf(req->pool, "%s, %*s",
53 msg_ret, len, (char *)text.value);
55 msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
57 gss_release_buffer(&min_ret, &text);
58 } while (msg_ctx != 0);
63 static char *mag_error(request_rec *req, const char *msg,
64 uint32_t maj, uint32_t min)
69 msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
70 msg_min = mag_status(req, GSS_C_MECH_CODE, min);
71 return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
74 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
76 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
77 apr_pool_t *temp, server_rec *s)
79 /* FIXME: create mutex to deal with connections and contexts ? */
80 mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
81 mag_post_config_session();
82 ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
87 static int mag_pre_connection(conn_rec *c, void *csd)
91 mc = apr_pcalloc(c->pool, sizeof(struct mag_conn));
94 ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
98 static apr_status_t mag_conn_destroy(void *ptr)
100 struct mag_conn *mc = (struct mag_conn *)ptr;
104 (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
105 mc->established = false;
110 static bool mag_conn_is_https(conn_rec *c)
113 if (mag_is_https(c)) return true;
119 static bool mag_acquire_creds(request_rec *req,
120 struct mag_config *cfg,
121 gss_OID_set desired_mechs,
122 gss_cred_usage_t cred_usage,
123 gss_cred_id_t *creds)
126 #ifdef HAVE_CRED_STORE
127 gss_const_key_value_set_t store = cfg->cred_store;
129 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
130 desired_mechs, cred_usage, store, creds,
133 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
134 desired_mechs, cred_usage, creds, NULL, NULL);
137 if (GSS_ERROR(maj)) {
138 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
139 mag_error(req, "gss_acquire_cred[_from]() "
140 "failed to get server creds",
148 #ifdef HAVE_CRED_STORE
149 static char *escape(apr_pool_t *pool, const char *name,
150 char find, const char *replace)
152 char *escaped = NULL;
157 namecopy = apr_pstrdup(pool, name);
159 p = strchr(namecopy, find);
160 if (!p) return namecopy;
165 /* terminate previous segment */
168 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
170 escaped = apr_pstrcat(pool, n, replace, NULL);
172 /* move to next segment */
176 /* append last segment if any */
178 escaped = apr_pstrcat(pool, escaped, n, NULL);
184 static void mag_store_deleg_creds(request_rec *req,
185 char *dir, char *clientname,
186 gss_cred_id_t delegated_cred,
189 gss_key_value_element_desc element;
190 gss_key_value_set_desc store;
195 /* We need to escape away '/', we can't have path separators in
196 * a ccache file name */
197 /* first double escape the esacping char (~) if any */
198 escaped = escape(req->pool, clientname, '~', "~~");
199 if (!escaped) return;
200 /* then escape away the separator (/) if any */
201 escaped = escape(req->pool, escaped, '/', "~");
202 if (!escaped) return;
204 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
206 element.key = "ccache";
207 element.value = value;
208 store.elements = &element;
211 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
212 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
213 if (GSS_ERROR(maj)) {
214 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
215 mag_error(req, "failed to store delegated creds",
223 static int mag_auth(request_rec *req)
226 const char *auth_type;
227 struct mag_config *cfg;
228 const char *auth_header;
229 char *auth_header_type;
230 char *auth_header_value;
231 int ret = HTTP_UNAUTHORIZED;
232 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
234 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
235 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
236 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
237 gss_name_t client = GSS_C_NO_NAME;
238 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
239 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
240 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
241 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
242 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
249 gss_OID mech_type = GSS_C_NO_OID;
250 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
251 struct mag_conn *mc = NULL;
252 bool is_basic = false;
253 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
254 gss_name_t server = GSS_C_NO_NAME;
255 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
256 const char *user_ccache = NULL;
257 const char *orig_ccache = NULL;
259 uint32_t init_flags = 0;
262 type = ap_auth_type(req);
263 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
267 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
269 /* implicit auth for subrequests if main auth already happened */
270 if (!ap_is_initial_req(req)) {
271 type = ap_auth_type(req->main);
272 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
273 /* warn if the subrequest location and the main request
274 * location have different configs */
275 if (cfg != ap_get_module_config(req->main->per_dir_config,
276 &auth_gssapi_module)) {
277 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
278 req, "Subrequest authentication bypass on "
279 "location with different configuration!");
281 if (req->main->user) {
282 req->user = apr_pstrdup(req->pool, req->main->user);
285 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
286 "The main request is tasked to establish the "
287 "security context, can't proceed!");
288 return HTTP_UNAUTHORIZED;
291 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
292 "Subrequest GSSAPI auth with no auth on the main "
293 "request. This operation may fail if other "
294 "subrequests already established a context or the "
295 "mechanism requires multiple roundtrips.");
300 if (!mag_conn_is_https(req->connection)) {
301 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
302 "Not a TLS connection, refusing to authenticate!");
307 if (cfg->gss_conn_ctx) {
308 mc = (struct mag_conn *)ap_get_module_config(
309 req->connection->conn_config,
310 &auth_gssapi_module);
312 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
313 "Failed to retrieve connection context!");
318 /* if available, session always supersedes connection bound data */
319 if (cfg->use_sessions) {
320 mag_check_session(req, cfg, &mc);
324 /* register the context in the memory pool, so it can be freed
325 * when the connection/request is terminated */
326 apr_pool_userdata_set(mc, "mag_conn_ptr",
327 mag_conn_destroy, mc->parent);
329 if (mc->established) {
330 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
331 "Already established context found!");
332 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
333 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
334 apr_psprintf(req->pool,
335 "%ld", (long)mc->expiration));
336 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
337 req->user = apr_pstrdup(req->pool, mc->user_name);
346 auth_header = apr_table_get(req->headers_in, "Authorization");
347 if (!auth_header) goto done;
349 auth_header_type = ap_getword_white(req->pool, &auth_header);
350 if (!auth_header_type) goto done;
352 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
353 auth_type = "Negotiate";
355 auth_header_value = ap_getword_white(req->pool, &auth_header);
356 if (!auth_header_value) goto done;
357 input.length = apr_base64_decode_len(auth_header_value) + 1;
358 input.value = apr_pcalloc(req->pool, input.length);
359 if (!input.value) goto done;
360 input.length = apr_base64_decode(input.value, auth_header_value);
361 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
362 (cfg->use_basic_auth == true)) {
366 gss_buffer_desc ba_user;
367 gss_buffer_desc ba_pwd;
369 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
370 if (!ba_pwd.value) goto done;
371 ba_user.value = ap_getword_nulls_nc(req->pool,
372 (char **)&ba_pwd.value, ':');
373 if (!ba_user.value) goto done;
374 if (((char *)ba_user.value)[0] == '\0' ||
375 ((char *)ba_pwd.value)[0] == '\0') {
376 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
377 "Invalid empty user or password for Basic Auth");
380 ba_user.length = strlen(ba_user.value);
381 ba_pwd.length = strlen(ba_pwd.value);
382 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
383 if (GSS_ERROR(maj)) {
384 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
386 mag_error(req, "gss_import_name() failed",
390 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
391 /* Set a per-thread ccache in case we are using kerberos,
392 * it is not elegant but avoids interference between threads */
393 long long unsigned int rndname;
395 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
396 sizeof(long long unsigned int));
397 if (rs != APR_SUCCESS) {
398 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
399 "Failed to generate random ccache name");
402 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
403 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
404 if (GSS_ERROR(maj)) {
405 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
407 mag_error(req, "gss_krb5_ccache_name() "
408 "failed", maj, min));
412 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
416 &user_cred, NULL, NULL);
417 if (GSS_ERROR(maj)) {
418 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
420 mag_error(req, "gss_acquire_cred_with_password() "
421 "failed", maj, min));
424 gss_release_name(&min, &client);
429 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
431 #ifdef HAVE_CRED_STORE
432 if (cfg->use_s4u2proxy) {
433 cred_usage = GSS_C_BOTH;
436 if (!mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET,
437 cred_usage, &acquired_cred)) {
442 if (cred_usage == GSS_C_BOTH) {
443 /* If GSS_C_BOTH is used then inquire_cred will return the client
444 * name instead of the SPN of the server credentials. Therefore we
445 * need to acquire a different set of credential setting
446 * GSS_C_ACCEPT explicitly */
447 if (!mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET,
448 GSS_C_ACCEPT, &server_cred)) {
452 server_cred = acquired_cred;
454 maj = gss_inquire_cred(&min, server_cred, &server,
456 if (GSS_ERROR(maj)) {
457 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
458 "%s", mag_error(req, "gss_inquired_cred_() "
459 "failed", maj, min));
462 if (server_cred != acquired_cred) {
463 gss_release_cred(&min, &server_cred);
466 #ifdef HAVE_CRED_STORE
467 if (cfg->deleg_ccache_dir) {
468 /* delegate ourselves credentials so we store them as requested */
469 init_flags |= GSS_C_DELEG_FLAG;
473 /* output and input are inverted here, this is intentional */
474 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
475 GSS_C_NO_OID, init_flags, 300,
476 GSS_C_NO_CHANNEL_BINDINGS, &output,
477 NULL, &input, NULL, NULL);
478 if (GSS_ERROR(maj)) {
479 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
480 "%s", mag_error(req, "gss_init_sec_context() "
481 "failed", maj, min));
486 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
487 &input, GSS_C_NO_CHANNEL_BINDINGS,
488 &client, &mech_type, &output, &flags, &vtime,
490 if (GSS_ERROR(maj)) {
491 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
492 mag_error(req, "gss_accept_sec_context() failed",
497 while (maj == GSS_S_CONTINUE_NEEDED) {
498 gss_release_buffer(&min, &input);
499 /* output and input are inverted here, this is intentional */
500 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
501 GSS_C_NO_OID, init_flags, 300,
502 GSS_C_NO_CHANNEL_BINDINGS, &output,
503 NULL, &input, NULL, NULL);
504 if (GSS_ERROR(maj)) {
505 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
506 "%s", mag_error(req, "gss_init_sec_context() "
507 "failed", maj, min));
510 gss_release_buffer(&min, &output);
511 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
512 &input, GSS_C_NO_CHANNEL_BINDINGS,
513 &client, &mech_type, &output, &flags,
514 &vtime, &delegated_cred);
515 if (GSS_ERROR(maj)) {
516 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
517 "%s", mag_error(req, "gss_accept_sec_context()"
518 " failed", maj, min));
522 } else if (maj == GSS_S_CONTINUE_NEEDED) {
524 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
525 "Mechanism needs continuation but neither "
526 "GssapiConnectionBound nor "
527 "GssapiUseSessions are available");
528 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
529 gss_release_buffer(&min, &output);
532 /* auth not complete send token and wait next packet */
536 /* Always set the GSS name in an env var */
537 maj = gss_display_name(&min, client, &name, NULL);
538 if (GSS_ERROR(maj)) {
539 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
540 mag_error(req, "gss_display_name() failed",
544 clientname = apr_pstrndup(req->pool, name.value, name.length);
545 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
546 expiration = time(NULL) + vtime;
547 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
548 apr_psprintf(req->pool, "%ld", (long)expiration));
550 #ifdef HAVE_CRED_STORE
551 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
552 char *ccachefile = NULL;
554 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
555 delegated_cred, &ccachefile);
558 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
563 if (cfg->map_to_local) {
564 maj = gss_localname(&min, client, mech_type, &lname);
565 if (maj != GSS_S_COMPLETE) {
566 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
567 mag_error(req, "gss_localname() failed", maj, min));
570 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
572 req->user = clientname;
576 mc->user_name = apr_pstrdup(mc->parent, req->user);
577 mc->gss_name = apr_pstrdup(mc->parent, clientname);
578 mc->established = true;
579 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
580 vtime = MIN_SESS_EXP_TIME;
582 mc->expiration = expiration;
583 if (cfg->use_sessions) {
584 mag_attempt_session(req, cfg, mc);
586 mc->auth_type = auth_type;
589 if (cfg->send_persist)
590 apr_table_set(req->headers_out, "Persistent-Auth",
591 cfg->gss_conn_ctx ? "true" : "false");
596 if ((!is_basic) && (output.length != 0)) {
597 replen = apr_base64_encode_len(output.length) + 1;
598 reply = apr_pcalloc(req->pool, 10 + replen);
600 memcpy(reply, "Negotiate ", 10);
601 apr_base64_encode(&reply[10], output.value, output.length);
602 apr_table_add(req->err_headers_out,
603 "WWW-Authenticate", reply);
605 } else if (ret == HTTP_UNAUTHORIZED) {
606 apr_table_add(req->err_headers_out,
607 "WWW-Authenticate", "Negotiate");
608 if (cfg->use_basic_auth) {
609 apr_table_add(req->err_headers_out,
611 apr_psprintf(req->pool, "Basic realm=\"%s\"",
615 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
616 if (user_ccache != NULL) {
617 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
618 if (maj != GSS_S_COMPLETE) {
619 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
620 "Failed to restore per-thread ccache, %s",
621 mag_error(req, "gss_krb5_ccache_name() "
622 "failed", maj, min));
626 gss_delete_sec_context(&min, &user_ctx, &output);
627 gss_release_cred(&min, &user_cred);
628 gss_release_cred(&min, &acquired_cred);
629 gss_release_cred(&min, &delegated_cred);
630 gss_release_buffer(&min, &output);
631 gss_release_name(&min, &client);
632 gss_release_name(&min, &server);
633 gss_release_buffer(&min, &name);
634 gss_release_buffer(&min, &lname);
639 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
641 struct mag_config *cfg;
643 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
649 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
651 struct mag_config *cfg = (struct mag_config *)mconfig;
652 cfg->ssl_only = on ? true : false;
656 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
658 struct mag_config *cfg = (struct mag_config *)mconfig;
659 cfg->map_to_local = on ? true : false;
663 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
665 struct mag_config *cfg = (struct mag_config *)mconfig;
666 cfg->gss_conn_ctx = on ? true : false;
670 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
672 struct mag_config *cfg = (struct mag_config *)mconfig;
673 cfg->send_persist = on ? true : false;
677 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
679 struct mag_config *cfg = (struct mag_config *)mconfig;
680 cfg->use_sessions = on ? true : false;
684 #ifdef HAVE_CRED_STORE
685 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
687 struct mag_config *cfg = (struct mag_config *)mconfig;
688 cfg->use_s4u2proxy = on ? true : false;
690 if (cfg->deleg_ccache_dir == NULL) {
691 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
697 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
699 struct mag_config *cfg = (struct mag_config *)mconfig;
706 if (strncmp(w, "key:", 4) != 0) {
707 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
708 "Invalid key format, expected prefix 'key:'");
713 l = apr_base64_decode_len(k);
714 val = apr_palloc(parms->temp_pool, l);
716 keys.length = (int)apr_base64_decode_binary(val, k);
717 keys.value = (unsigned char *)val;
719 if (keys.length != 32) {
720 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
721 "Invalid key length, expected 32 got %d", keys.length);
725 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
727 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
728 "Failed to import sealing key!");
733 #ifdef HAVE_CRED_STORE
735 #define MAX_CRED_OPTIONS 10
737 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
740 struct mag_config *cfg = (struct mag_config *)mconfig;
741 gss_key_value_element_desc *elements;
750 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
751 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
755 key = apr_pstrndup(parms->pool, w, (p-w));
756 value = apr_pstrdup(parms->pool, p + 1);
758 if (!cfg->cred_store) {
759 cfg->cred_store = apr_pcalloc(parms->pool,
760 sizeof(gss_key_value_set_desc));
761 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
762 cfg->cred_store->elements = apr_palloc(parms->pool, size);
765 elements = cfg->cred_store->elements;
766 count = cfg->cred_store->count;
768 if (count >= MAX_CRED_OPTIONS) {
769 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
770 "Too many GssapiCredStore options (MAX: %d)",
774 cfg->cred_store->count++;
776 elements[count].key = key;
777 elements[count].value = value;
782 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
785 struct mag_config *cfg = (struct mag_config *)mconfig;
787 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
793 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
795 struct mag_config *cfg = (struct mag_config *)mconfig;
797 cfg->use_basic_auth = on ? true : false;
801 static const command_rec mag_commands[] = {
802 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
803 "Work only if connection is SSL Secured"),
804 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
805 "Translate principals to local names"),
806 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
807 "Authentication is bound to the TCP connection"),
808 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
809 "Send Persitent-Auth header according to connection bound"),
810 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
811 "Authentication uses mod_sessions to hold status"),
812 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
813 "Key Used to seal session data."),
814 #ifdef HAVE_CRED_STORE
815 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
816 "Initializes credentials for s4u2proxy usage"),
817 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
819 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
820 OR_AUTHCFG, "Directory to store delegated credentials"),
822 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
823 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
824 "Allows use of Basic Auth for authentication"),
830 mag_register_hooks(apr_pool_t *p)
832 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
833 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
834 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
837 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
839 STANDARD20_MODULE_STUFF,
840 mag_create_dir_config,