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 #ifdef HAVE_CRED_STORE
120 static char *escape(apr_pool_t *pool, const char *name,
121 char find, const char *replace)
123 char *escaped = NULL;
128 namecopy = apr_pstrdup(pool, name);
130 p = strchr(namecopy, find);
131 if (!p) return namecopy;
136 /* terminate previous segment */
139 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
141 escaped = apr_pstrcat(pool, n, replace, NULL);
143 /* move to next segment */
147 /* append last segment if any */
149 escaped = apr_pstrcat(pool, escaped, n, NULL);
155 static void mag_store_deleg_creds(request_rec *req,
156 char *dir, char *clientname,
157 gss_cred_id_t delegated_cred,
160 gss_key_value_element_desc element;
161 gss_key_value_set_desc store;
166 /* We need to escape away '/', we can't have path separators in
167 * a ccache file name */
168 /* first double escape the esacping char (~) if any */
169 escaped = escape(req->pool, clientname, '~', "~~");
170 if (!escaped) return;
171 /* then escape away the separator (/) if any */
172 escaped = escape(req->pool, escaped, '/', "~");
173 if (!escaped) return;
175 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
177 element.key = "ccache";
178 element.value = value;
179 store.elements = &element;
182 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
183 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
184 if (GSS_ERROR(maj)) {
185 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
186 mag_error(req, "failed to store delegated creds",
194 static int mag_auth(request_rec *req)
197 const char *auth_type;
198 struct mag_config *cfg;
199 const char *auth_header;
200 char *auth_header_type;
201 char *auth_header_value;
202 int ret = HTTP_UNAUTHORIZED;
203 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
205 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
206 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
207 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
208 gss_name_t client = GSS_C_NO_NAME;
209 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
210 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
211 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
212 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
213 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
220 gss_OID mech_type = GSS_C_NO_OID;
221 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
222 struct mag_conn *mc = NULL;
223 bool is_basic = false;
224 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
225 gss_name_t server = GSS_C_NO_NAME;
226 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
227 const char *user_ccache = NULL;
228 const char *orig_ccache = NULL;
230 uint32_t init_flags = 0;
233 type = ap_auth_type(req);
234 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
238 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
240 /* implicit auth for subrequests if main auth already happened */
241 if (!ap_is_initial_req(req)) {
242 type = ap_auth_type(req->main);
243 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
244 /* warn if the subrequest location and the main request
245 * location have different configs */
246 if (cfg != ap_get_module_config(req->main->per_dir_config,
247 &auth_gssapi_module)) {
248 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
249 req, "Subrequest authentication bypass on "
250 "location with different configuration!");
252 if (req->main->user) {
253 req->user = apr_pstrdup(req->pool, req->main->user);
256 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
257 "The main request is tasked to establish the "
258 "security context, can't proceed!");
259 return HTTP_UNAUTHORIZED;
262 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
263 "Subrequest GSSAPI auth with no auth on the main "
264 "request. This operation may fail if other "
265 "subrequests already established a context or the "
266 "mechanism requires multiple roundtrips.");
271 if (!mag_conn_is_https(req->connection)) {
272 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
273 "Not a TLS connection, refusing to authenticate!");
278 if (cfg->gss_conn_ctx) {
279 mc = (struct mag_conn *)ap_get_module_config(
280 req->connection->conn_config,
281 &auth_gssapi_module);
283 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
284 "Failed to retrieve connection context!");
289 /* if available, session always supersedes connection bound data */
290 if (cfg->use_sessions) {
291 mag_check_session(req, cfg, &mc);
295 /* register the context in the memory pool, so it can be freed
296 * when the connection/request is terminated */
297 apr_pool_userdata_set(mc, "mag_conn_ptr",
298 mag_conn_destroy, mc->parent);
300 if (mc->established) {
301 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
302 "Already established context found!");
303 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
304 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
305 apr_psprintf(req->pool,
306 "%ld", (long)mc->expiration));
307 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
308 req->user = apr_pstrdup(req->pool, mc->user_name);
317 auth_header = apr_table_get(req->headers_in, "Authorization");
318 if (!auth_header) goto done;
320 auth_header_type = ap_getword_white(req->pool, &auth_header);
321 if (!auth_header_type) goto done;
323 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
324 auth_type = "Negotiate";
326 auth_header_value = ap_getword_white(req->pool, &auth_header);
327 if (!auth_header_value) goto done;
328 input.length = apr_base64_decode_len(auth_header_value) + 1;
329 input.value = apr_pcalloc(req->pool, input.length);
330 if (!input.value) goto done;
331 input.length = apr_base64_decode(input.value, auth_header_value);
332 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
333 (cfg->use_basic_auth == true)) {
337 gss_buffer_desc ba_user;
338 gss_buffer_desc ba_pwd;
340 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
341 if (!ba_pwd.value) goto done;
342 ba_user.value = ap_getword_nulls_nc(req->pool,
343 (char **)&ba_pwd.value, ':');
344 if (!ba_user.value) goto done;
345 if (((char *)ba_user.value)[0] == '\0' ||
346 ((char *)ba_pwd.value)[0] == '\0') {
347 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
348 "Invalid empty user or password for Basic Auth");
351 ba_user.length = strlen(ba_user.value);
352 ba_pwd.length = strlen(ba_pwd.value);
353 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
354 if (GSS_ERROR(maj)) {
355 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
357 mag_error(req, "gss_import_name() failed",
361 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
362 /* Set a per-thread ccache in case we are using kerberos,
363 * it is not elegant but avoids interference between threads */
364 long long unsigned int rndname;
366 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
367 sizeof(long long unsigned int));
368 if (rs != APR_SUCCESS) {
369 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
370 "Failed to generate random ccache name");
373 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
374 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
375 if (GSS_ERROR(maj)) {
376 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
378 mag_error(req, "gss_krb5_ccache_name() "
379 "failed", maj, min));
383 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
387 &user_cred, NULL, NULL);
388 if (GSS_ERROR(maj)) {
389 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
391 mag_error(req, "gss_acquire_cred_with_password() "
392 "failed", maj, min));
395 gss_release_name(&min, &client);
400 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
402 #ifdef HAVE_CRED_STORE
403 if (cfg->use_s4u2proxy) {
404 cred_usage = GSS_C_BOTH;
406 if (cfg->cred_store) {
407 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
408 GSS_C_NO_OID_SET, cred_usage,
409 cfg->cred_store, &acquired_cred,
411 if (GSS_ERROR(maj)) {
412 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
413 mag_error(req, "gss_acquire_cred_from() failed",
421 if (!acquired_cred) {
422 /* Try to acquire default creds */
423 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
424 GSS_C_NO_OID_SET, cred_usage,
425 &acquired_cred, NULL, NULL);
426 if (GSS_ERROR(maj)) {
427 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
428 "%s", mag_error(req, "gss_acquire_cred()"
429 " failed", maj, min));
433 if (cred_usage == GSS_C_BOTH) {
434 /* If GSS_C_BOTH is used then inquire_cred will return the client
435 * name instead of the SPN of the server credentials. Therefore we
436 * need to acquire a different set of credential setting
437 * GSS_C_ACCEPT explicitly */
438 #ifdef HAVE_CRED_STORE
439 if (cfg->cred_store) {
440 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME,
441 GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
442 GSS_C_ACCEPT, cfg->cred_store,
443 &server_cred, NULL, NULL);
448 /* Try to acquire default creds */
449 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
450 GSS_C_NO_OID_SET, GSS_C_ACCEPT,
451 &server_cred, NULL, NULL);
453 if (GSS_ERROR(maj)) {
454 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
455 mag_error(req, "gss_acquire_cred[_from]() "
456 "failed to get server creds",
461 server_cred = acquired_cred;
463 maj = gss_inquire_cred(&min, server_cred, &server,
465 if (GSS_ERROR(maj)) {
466 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
467 "%s", mag_error(req, "gss_inquired_cred_() "
468 "failed", maj, min));
471 if (server_cred != acquired_cred) {
472 gss_release_cred(&min, &server_cred);
475 #ifdef HAVE_CRED_STORE
476 if (cfg->deleg_ccache_dir) {
477 /* delegate ourselves credentials so we store them as requested */
478 init_flags |= GSS_C_DELEG_FLAG;
482 /* output and input are inverted here, this is intentional */
483 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
484 GSS_C_NO_OID, init_flags, 300,
485 GSS_C_NO_CHANNEL_BINDINGS, &output,
486 NULL, &input, NULL, NULL);
487 if (GSS_ERROR(maj)) {
488 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
489 "%s", mag_error(req, "gss_init_sec_context() "
490 "failed", maj, min));
495 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
496 &input, GSS_C_NO_CHANNEL_BINDINGS,
497 &client, &mech_type, &output, &flags, &vtime,
499 if (GSS_ERROR(maj)) {
500 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
501 mag_error(req, "gss_accept_sec_context() failed",
506 while (maj == GSS_S_CONTINUE_NEEDED) {
507 gss_release_buffer(&min, &input);
508 /* output and input are inverted here, this is intentional */
509 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
510 GSS_C_NO_OID, init_flags, 300,
511 GSS_C_NO_CHANNEL_BINDINGS, &output,
512 NULL, &input, NULL, NULL);
513 if (GSS_ERROR(maj)) {
514 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
515 "%s", mag_error(req, "gss_init_sec_context() "
516 "failed", maj, min));
519 gss_release_buffer(&min, &output);
520 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
521 &input, GSS_C_NO_CHANNEL_BINDINGS,
522 &client, &mech_type, &output, &flags,
523 &vtime, &delegated_cred);
524 if (GSS_ERROR(maj)) {
525 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
526 "%s", mag_error(req, "gss_accept_sec_context()"
527 " failed", maj, min));
531 } else if (maj == GSS_S_CONTINUE_NEEDED) {
533 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
534 "Mechanism needs continuation but neither "
535 "GssapiConnectionBound nor "
536 "GssapiUseSessions are available");
537 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
538 gss_release_buffer(&min, &output);
541 /* auth not complete send token and wait next packet */
545 /* Always set the GSS name in an env var */
546 maj = gss_display_name(&min, client, &name, NULL);
547 if (GSS_ERROR(maj)) {
548 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
549 mag_error(req, "gss_display_name() failed",
553 clientname = apr_pstrndup(req->pool, name.value, name.length);
554 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
555 expiration = time(NULL) + vtime;
556 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
557 apr_psprintf(req->pool, "%ld", (long)expiration));
559 #ifdef HAVE_CRED_STORE
560 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
561 char *ccachefile = NULL;
563 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
564 delegated_cred, &ccachefile);
567 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
572 if (cfg->map_to_local) {
573 maj = gss_localname(&min, client, mech_type, &lname);
574 if (maj != GSS_S_COMPLETE) {
575 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
576 mag_error(req, "gss_localname() failed", maj, min));
579 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
581 req->user = clientname;
585 mc->user_name = apr_pstrdup(mc->parent, req->user);
586 mc->gss_name = apr_pstrdup(mc->parent, clientname);
587 mc->established = true;
588 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
589 vtime = MIN_SESS_EXP_TIME;
591 mc->expiration = expiration;
592 if (cfg->use_sessions) {
593 mag_attempt_session(req, cfg, mc);
595 mc->auth_type = auth_type;
598 if (cfg->send_persist)
599 apr_table_set(req->headers_out, "Persistent-Auth",
600 cfg->gss_conn_ctx ? "true" : "false");
605 if ((!is_basic) && (output.length != 0)) {
606 replen = apr_base64_encode_len(output.length) + 1;
607 reply = apr_pcalloc(req->pool, 10 + replen);
609 memcpy(reply, "Negotiate ", 10);
610 apr_base64_encode(&reply[10], output.value, output.length);
611 apr_table_add(req->err_headers_out,
612 "WWW-Authenticate", reply);
614 } else if (ret == HTTP_UNAUTHORIZED) {
615 apr_table_add(req->err_headers_out,
616 "WWW-Authenticate", "Negotiate");
617 if (cfg->use_basic_auth) {
618 apr_table_add(req->err_headers_out,
620 apr_psprintf(req->pool, "Basic realm=\"%s\"",
624 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
625 if (user_ccache != NULL) {
626 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
627 if (maj != GSS_S_COMPLETE) {
628 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
629 "Failed to restore per-thread ccache, %s",
630 mag_error(req, "gss_krb5_ccache_name() "
631 "failed", maj, min));
635 gss_delete_sec_context(&min, &user_ctx, &output);
636 gss_release_cred(&min, &user_cred);
637 gss_release_cred(&min, &acquired_cred);
638 gss_release_cred(&min, &delegated_cred);
639 gss_release_buffer(&min, &output);
640 gss_release_name(&min, &client);
641 gss_release_name(&min, &server);
642 gss_release_buffer(&min, &name);
643 gss_release_buffer(&min, &lname);
648 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
650 struct mag_config *cfg;
652 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
658 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
660 struct mag_config *cfg = (struct mag_config *)mconfig;
661 cfg->ssl_only = on ? true : false;
665 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
667 struct mag_config *cfg = (struct mag_config *)mconfig;
668 cfg->map_to_local = on ? true : false;
672 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
674 struct mag_config *cfg = (struct mag_config *)mconfig;
675 cfg->gss_conn_ctx = on ? true : false;
679 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
681 struct mag_config *cfg = (struct mag_config *)mconfig;
682 cfg->send_persist = on ? true : false;
686 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
688 struct mag_config *cfg = (struct mag_config *)mconfig;
689 cfg->use_sessions = on ? true : false;
693 #ifdef HAVE_CRED_STORE
694 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
696 struct mag_config *cfg = (struct mag_config *)mconfig;
697 cfg->use_s4u2proxy = on ? true : false;
699 if (cfg->deleg_ccache_dir == NULL) {
700 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
706 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
708 struct mag_config *cfg = (struct mag_config *)mconfig;
715 if (strncmp(w, "key:", 4) != 0) {
716 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
717 "Invalid key format, expected prefix 'key:'");
722 l = apr_base64_decode_len(k);
723 val = apr_palloc(parms->temp_pool, l);
725 keys.length = (int)apr_base64_decode_binary(val, k);
726 keys.value = (unsigned char *)val;
728 if (keys.length != 32) {
729 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
730 "Invalid key length, expected 32 got %d", keys.length);
734 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
736 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
737 "Failed to import sealing key!");
742 #ifdef HAVE_CRED_STORE
744 #define MAX_CRED_OPTIONS 10
746 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
749 struct mag_config *cfg = (struct mag_config *)mconfig;
750 gss_key_value_element_desc *elements;
759 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
760 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
764 key = apr_pstrndup(parms->pool, w, (p-w));
765 value = apr_pstrdup(parms->pool, p + 1);
767 if (!cfg->cred_store) {
768 cfg->cred_store = apr_pcalloc(parms->pool,
769 sizeof(gss_key_value_set_desc));
770 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
771 cfg->cred_store->elements = apr_palloc(parms->pool, size);
774 elements = cfg->cred_store->elements;
775 count = cfg->cred_store->count;
777 if (count >= MAX_CRED_OPTIONS) {
778 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
779 "Too many GssapiCredStore options (MAX: %d)",
783 cfg->cred_store->count++;
785 elements[count].key = key;
786 elements[count].value = value;
791 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
794 struct mag_config *cfg = (struct mag_config *)mconfig;
796 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
802 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
804 struct mag_config *cfg = (struct mag_config *)mconfig;
806 cfg->use_basic_auth = on ? true : false;
810 static const command_rec mag_commands[] = {
811 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
812 "Work only if connection is SSL Secured"),
813 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
814 "Translate principals to local names"),
815 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
816 "Authentication is bound to the TCP connection"),
817 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
818 "Send Persitent-Auth header according to connection bound"),
819 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
820 "Authentication uses mod_sessions to hold status"),
821 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
822 "Key Used to seal session data."),
823 #ifdef HAVE_CRED_STORE
824 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
825 "Initializes credentials for s4u2proxy usage"),
826 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
828 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
829 OR_AUTHCFG, "Directory to store delegated credentials"),
831 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
832 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
833 "Allows use of Basic Auth for authentication"),
839 mag_register_hooks(apr_pool_t *p)
841 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
842 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
843 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
846 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
848 STANDARD20_MODULE_STUFF,
849 mag_create_dir_config,