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 char *escape(apr_pool_t *pool, const char *name,
120 char find, const char *replace)
122 char *escaped = NULL;
127 namecopy = apr_pstrdup(pool, name);
129 p = strchr(namecopy, find);
130 if (!p) return namecopy;
135 /* terminate previous segment */
138 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
140 escaped = apr_pstrcat(pool, n, replace, NULL);
142 /* move to next segment */
146 /* append last segment if any */
148 escaped = apr_pstrcat(pool, escaped, n, NULL);
154 static void mag_store_deleg_creds(request_rec *req,
155 char *dir, char *clientname,
156 gss_cred_id_t delegated_cred,
159 gss_key_value_element_desc element;
160 gss_key_value_set_desc store;
165 /* We need to escape away '/', we can't have path separators in
166 * a ccache file name */
167 /* first double escape the esacping char (~) if any */
168 escaped = escape(req->pool, clientname, '~', "~~");
169 if (!escaped) return;
170 /* then escape away the separator (/) if any */
171 escaped = escape(req->pool, escaped, '/', "~");
172 if (!escaped) return;
174 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
176 element.key = "ccache";
177 element.value = value;
178 store.elements = &element;
181 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
182 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
183 if (GSS_ERROR(maj)) {
184 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
185 mag_error(req, "failed to store delegated creds",
192 static int mag_auth(request_rec *req)
195 const char *auth_type;
196 struct mag_config *cfg;
197 const char *auth_header;
198 char *auth_header_type;
199 char *auth_header_value;
200 int ret = HTTP_UNAUTHORIZED;
201 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
203 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
204 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
205 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
206 gss_name_t client = GSS_C_NO_NAME;
207 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
208 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
209 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
210 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
211 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
218 gss_OID mech_type = GSS_C_NO_OID;
219 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
220 struct mag_conn *mc = NULL;
221 bool is_basic = false;
222 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
223 gss_name_t server = GSS_C_NO_NAME;
224 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
225 const char *user_ccache = NULL;
226 const char *orig_ccache = NULL;
228 uint32_t init_flags = 0;
231 type = ap_auth_type(req);
232 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
236 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
238 /* implicit auth for subrequests if main auth already happened */
239 if (!ap_is_initial_req(req)) {
240 type = ap_auth_type(req->main);
241 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
242 /* warn if the subrequest location and the main request
243 * location have different configs */
244 if (cfg != ap_get_module_config(req->main->per_dir_config,
245 &auth_gssapi_module)) {
246 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
247 req, "Subrequest authentication bypass on "
248 "location with different configuration!");
250 if (req->main->user) {
251 req->user = apr_pstrdup(req->pool, req->main->user);
254 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
255 "The main request is tasked to establish the "
256 "security context, can't proceed!");
257 return HTTP_UNAUTHORIZED;
260 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
261 "Subrequest GSSAPI auth with no auth on the main "
262 "request. This operation may fail if other "
263 "subrequests already established a context or the "
264 "mechanism requires multiple roundtrips.");
269 if (!mag_conn_is_https(req->connection)) {
270 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
271 "Not a TLS connection, refusing to authenticate!");
276 if (cfg->gss_conn_ctx) {
277 mc = (struct mag_conn *)ap_get_module_config(
278 req->connection->conn_config,
279 &auth_gssapi_module);
281 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
282 "Failed to retrieve connection context!");
287 /* if available, session always supersedes connection bound data */
288 if (cfg->use_sessions) {
289 mag_check_session(req, cfg, &mc);
293 /* register the context in the memory pool, so it can be freed
294 * when the connection/request is terminated */
295 apr_pool_userdata_set(mc, "mag_conn_ptr",
296 mag_conn_destroy, mc->parent);
298 if (mc->established) {
299 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
300 "Already established context found!");
301 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
302 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
303 apr_psprintf(req->pool,
304 "%ld", (long)mc->expiration));
305 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
306 req->user = apr_pstrdup(req->pool, mc->user_name);
315 auth_header = apr_table_get(req->headers_in, "Authorization");
316 if (!auth_header) goto done;
318 auth_header_type = ap_getword_white(req->pool, &auth_header);
319 if (!auth_header_type) goto done;
321 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
322 auth_type = "Negotiate";
324 auth_header_value = ap_getword_white(req->pool, &auth_header);
325 if (!auth_header_value) goto done;
326 input.length = apr_base64_decode_len(auth_header_value) + 1;
327 input.value = apr_pcalloc(req->pool, input.length);
328 if (!input.value) goto done;
329 input.length = apr_base64_decode(input.value, auth_header_value);
330 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
331 (cfg->use_basic_auth == true)) {
335 gss_buffer_desc ba_user;
336 gss_buffer_desc ba_pwd;
338 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
339 if (!ba_pwd.value) goto done;
340 ba_user.value = ap_getword_nulls_nc(req->pool,
341 (char **)&ba_pwd.value, ':');
342 if (!ba_user.value) goto done;
343 if (((char *)ba_user.value)[0] == '\0' ||
344 ((char *)ba_pwd.value)[0] == '\0') {
345 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
346 "Invalid empty user or password for Basic Auth");
349 ba_user.length = strlen(ba_user.value);
350 ba_pwd.length = strlen(ba_pwd.value);
351 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
352 if (GSS_ERROR(maj)) {
353 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
355 mag_error(req, "gss_import_name() failed",
359 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
360 /* Set a per-thread ccache in case we are using kerberos,
361 * it is not elegant but avoids interference between threads */
362 long long unsigned int rndname;
364 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
365 sizeof(long long unsigned int));
366 if (rs != APR_SUCCESS) {
367 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
368 "Failed to generate random ccache name");
371 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
372 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
373 if (GSS_ERROR(maj)) {
374 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
376 mag_error(req, "gss_krb5_ccache_name() "
377 "failed", maj, min));
381 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
385 &user_cred, NULL, NULL);
386 if (GSS_ERROR(maj)) {
387 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
389 mag_error(req, "gss_acquire_cred_with_password() "
390 "failed", maj, min));
393 gss_release_name(&min, &client);
398 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
400 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
401 if (cfg->use_s4u2proxy) {
402 cred_usage = GSS_C_BOTH;
404 if (cfg->cred_store) {
405 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
406 GSS_C_NO_OID_SET, cred_usage,
407 cfg->cred_store, &acquired_cred,
409 if (GSS_ERROR(maj)) {
410 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
411 mag_error(req, "gss_acquire_cred_from() failed",
419 if (!acquired_cred) {
420 /* Try to acquire default creds */
421 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
422 GSS_C_NO_OID_SET, cred_usage,
423 &acquired_cred, NULL, NULL);
424 if (GSS_ERROR(maj)) {
425 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
426 "%s", mag_error(req, "gss_acquire_cred()"
427 " failed", maj, min));
431 if (cred_usage == GSS_C_BOTH) {
432 /* If GSS_C_BOTH is used then inquire_cred will return the client
433 * name instead of the SPN of the server credentials. Therefore we
434 * need to acquire a different set of credential setting
435 * GSS_C_ACCEPT explicitly */
436 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
437 if (cfg->cred_store) {
438 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME,
439 GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
440 GSS_C_ACCEPT, cfg->cred_store,
441 &server_cred, NULL, NULL);
446 /* Try to acquire default creds */
447 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
448 GSS_C_NO_OID_SET, GSS_C_ACCEPT,
449 &server_cred, NULL, NULL);
451 if (GSS_ERROR(maj)) {
452 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
453 mag_error(req, "gss_acquire_cred[_from]() "
454 "failed to get server creds",
459 server_cred = acquired_cred;
461 maj = gss_inquire_cred(&min, server_cred, &server,
463 if (GSS_ERROR(maj)) {
464 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
465 "%s", mag_error(req, "gss_inquired_cred_() "
466 "failed", maj, min));
469 if (server_cred != acquired_cred) {
470 gss_release_cred(&min, &server_cred);
473 if (cfg->deleg_ccache_dir) {
474 /* delegate ourselves credentials so we store them as requested */
475 init_flags |= GSS_C_DELEG_FLAG;
478 /* output and input are inverted here, this is intentional */
479 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
480 GSS_C_NO_OID, init_flags, 300,
481 GSS_C_NO_CHANNEL_BINDINGS, &output,
482 NULL, &input, NULL, NULL);
483 if (GSS_ERROR(maj)) {
484 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
485 "%s", mag_error(req, "gss_init_sec_context() "
486 "failed", maj, min));
491 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
492 &input, GSS_C_NO_CHANNEL_BINDINGS,
493 &client, &mech_type, &output, &flags, &vtime,
495 if (GSS_ERROR(maj)) {
496 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
497 mag_error(req, "gss_accept_sec_context() failed",
502 while (maj == GSS_S_CONTINUE_NEEDED) {
503 gss_release_buffer(&min, &input);
504 /* output and input are inverted here, this is intentional */
505 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
506 GSS_C_NO_OID, init_flags, 300,
507 GSS_C_NO_CHANNEL_BINDINGS, &output,
508 NULL, &input, NULL, NULL);
509 if (GSS_ERROR(maj)) {
510 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
511 "%s", mag_error(req, "gss_init_sec_context() "
512 "failed", maj, min));
515 gss_release_buffer(&min, &output);
516 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
517 &input, GSS_C_NO_CHANNEL_BINDINGS,
518 &client, &mech_type, &output, &flags,
519 &vtime, &delegated_cred);
520 if (GSS_ERROR(maj)) {
521 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
522 "%s", mag_error(req, "gss_accept_sec_context()"
523 " failed", maj, min));
527 } else if (maj == GSS_S_CONTINUE_NEEDED) {
529 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
530 "Mechanism needs continuation but neither "
531 "GssapiConnectionBound nor "
532 "GssapiUseSessions are available");
533 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
534 gss_release_buffer(&min, &output);
537 /* auth not complete send token and wait next packet */
541 /* Always set the GSS name in an env var */
542 maj = gss_display_name(&min, client, &name, NULL);
543 if (GSS_ERROR(maj)) {
544 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
545 mag_error(req, "gss_display_name() failed",
549 clientname = apr_pstrndup(req->pool, name.value, name.length);
550 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
551 expiration = time(NULL) + vtime;
552 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
553 apr_psprintf(req->pool, "%ld", (long)expiration));
555 #ifdef HAVE_GSS_STORE_CRED_INTO
556 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
557 char *ccachefile = NULL;
559 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
560 delegated_cred, &ccachefile);
563 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
568 if (cfg->map_to_local) {
569 maj = gss_localname(&min, client, mech_type, &lname);
570 if (maj != GSS_S_COMPLETE) {
571 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
572 mag_error(req, "gss_localname() failed", maj, min));
575 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
577 req->user = clientname;
581 mc->user_name = apr_pstrdup(mc->parent, req->user);
582 mc->gss_name = apr_pstrdup(mc->parent, clientname);
583 mc->established = true;
584 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
585 vtime = MIN_SESS_EXP_TIME;
587 mc->expiration = expiration;
588 if (cfg->use_sessions) {
589 mag_attempt_session(req, cfg, mc);
591 mc->auth_type = auth_type;
594 if (cfg->send_persist)
595 apr_table_set(req->headers_out, "Persistent-Auth",
596 cfg->gss_conn_ctx ? "true" : "false");
601 if ((!is_basic) && (output.length != 0)) {
602 replen = apr_base64_encode_len(output.length) + 1;
603 reply = apr_pcalloc(req->pool, 10 + replen);
605 memcpy(reply, "Negotiate ", 10);
606 apr_base64_encode(&reply[10], output.value, output.length);
607 apr_table_add(req->err_headers_out,
608 "WWW-Authenticate", reply);
610 } else if (ret == HTTP_UNAUTHORIZED) {
611 apr_table_add(req->err_headers_out,
612 "WWW-Authenticate", "Negotiate");
613 if (cfg->use_basic_auth) {
614 apr_table_add(req->err_headers_out,
616 apr_psprintf(req->pool, "Basic realm=\"%s\"",
620 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
621 if (user_ccache != NULL) {
622 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
623 if (maj != GSS_S_COMPLETE) {
624 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
625 "Failed to restore per-thread ccache, %s",
626 mag_error(req, "gss_krb5_ccache_name() "
627 "failed", maj, min));
631 gss_delete_sec_context(&min, &user_ctx, &output);
632 gss_release_cred(&min, &user_cred);
633 gss_release_cred(&min, &acquired_cred);
634 gss_release_cred(&min, &delegated_cred);
635 gss_release_buffer(&min, &output);
636 gss_release_name(&min, &client);
637 gss_release_name(&min, &server);
638 gss_release_buffer(&min, &name);
639 gss_release_buffer(&min, &lname);
644 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
646 struct mag_config *cfg;
648 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
654 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
656 struct mag_config *cfg = (struct mag_config *)mconfig;
657 cfg->ssl_only = on ? true : false;
661 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
663 struct mag_config *cfg = (struct mag_config *)mconfig;
664 cfg->map_to_local = on ? true : false;
668 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
670 struct mag_config *cfg = (struct mag_config *)mconfig;
671 cfg->gss_conn_ctx = on ? true : false;
675 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
677 struct mag_config *cfg = (struct mag_config *)mconfig;
678 cfg->send_persist = on ? true : false;
682 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
684 struct mag_config *cfg = (struct mag_config *)mconfig;
685 cfg->use_sessions = on ? true : false;
689 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
691 struct mag_config *cfg = (struct mag_config *)mconfig;
692 cfg->use_s4u2proxy = on ? true : false;
694 if (cfg->deleg_ccache_dir == NULL) {
695 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
700 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
702 struct mag_config *cfg = (struct mag_config *)mconfig;
709 if (strncmp(w, "key:", 4) != 0) {
710 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
711 "Invalid key format, expected prefix 'key:'");
716 l = apr_base64_decode_len(k);
717 val = apr_palloc(parms->temp_pool, l);
719 keys.length = (int)apr_base64_decode_binary(val, k);
720 keys.value = (unsigned char *)val;
722 if (keys.length != 32) {
723 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
724 "Invalid key length, expected 32 got %d", keys.length);
728 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
730 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
731 "Failed to import sealing key!");
736 #define MAX_CRED_OPTIONS 10
738 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
741 struct mag_config *cfg = (struct mag_config *)mconfig;
742 gss_key_value_element_desc *elements;
751 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
752 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
756 key = apr_pstrndup(parms->pool, w, (p-w));
757 value = apr_pstrdup(parms->pool, p + 1);
759 if (!cfg->cred_store) {
760 cfg->cred_store = apr_pcalloc(parms->pool,
761 sizeof(gss_key_value_set_desc));
762 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
763 cfg->cred_store->elements = apr_palloc(parms->pool, size);
766 elements = cfg->cred_store->elements;
767 count = cfg->cred_store->count;
769 if (count >= MAX_CRED_OPTIONS) {
770 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
771 "Too many GssapiCredStore options (MAX: %d)",
775 cfg->cred_store->count++;
777 elements[count].key = key;
778 elements[count].value = value;
783 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
786 struct mag_config *cfg = (struct mag_config *)mconfig;
788 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_GSS_ACQUIRE_CRED_FROM
815 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
816 "Initializes credentials for s4u2proxy usage"),
818 #ifdef HAVE_GSS_STORE_CRED_INTO
819 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
821 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
822 OR_AUTHCFG, "Directory to store delegated credentials"),
824 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
825 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
826 "Allows use of Basic Auth for authentication"),
832 mag_register_hooks(apr_pool_t *p)
834 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
835 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
836 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
839 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
841 STANDARD20_MODULE_STUFF,
842 mag_create_dir_config,