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 const gss_OID_desc gss_mech_ntlmssp = {
28 GSS_NTLMSSP_OID_LENGTH, GSS_NTLMSSP_OID_STRING
31 const gss_OID_set_desc gss_mech_set_ntlmssp = {
32 1, discard_const(&gss_mech_ntlmssp)
35 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
37 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
39 APLOG_USE_MODULE(auth_gssapi);
41 static char *mag_status(request_rec *req, int type, uint32_t err)
43 uint32_t maj_ret, min_ret;
52 maj_ret = gss_display_status(&min_ret, err, type,
53 GSS_C_NO_OID, &msg_ctx, &text);
54 if (maj_ret != GSS_S_COMPLETE) {
60 msg_ret = apr_psprintf(req->pool, "%s, %*s",
61 msg_ret, len, (char *)text.value);
63 msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
65 gss_release_buffer(&min_ret, &text);
66 } while (msg_ctx != 0);
71 static char *mag_error(request_rec *req, const char *msg,
72 uint32_t maj, uint32_t min)
77 msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
78 msg_min = mag_status(req, GSS_C_MECH_CODE, min);
79 return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
82 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
84 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
85 apr_pool_t *temp, server_rec *s)
87 /* FIXME: create mutex to deal with connections and contexts ? */
88 mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
89 mag_post_config_session();
90 ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
95 static int mag_pre_connection(conn_rec *c, void *csd)
99 mc = apr_pcalloc(c->pool, sizeof(struct mag_conn));
101 mc->parent = c->pool;
102 ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
106 static apr_status_t mag_conn_destroy(void *ptr)
108 struct mag_conn *mc = (struct mag_conn *)ptr;
112 (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
113 mc->established = false;
118 static bool mag_conn_is_https(conn_rec *c)
121 if (mag_is_https(c)) return true;
127 static bool mag_acquire_creds(request_rec *req,
128 struct mag_config *cfg,
129 gss_OID_set desired_mechs,
130 gss_cred_usage_t cred_usage,
131 gss_cred_id_t *creds,
132 gss_OID_set *actual_mechs)
135 #ifdef HAVE_CRED_STORE
136 gss_const_key_value_set_t store = cfg->cred_store;
138 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
139 desired_mechs, cred_usage, store, creds,
142 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
143 desired_mechs, cred_usage, creds,
147 if (GSS_ERROR(maj)) {
148 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
149 mag_error(req, "gss_acquire_cred[_from]() "
150 "failed to get server creds",
158 #ifdef HAVE_CRED_STORE
159 static char *escape(apr_pool_t *pool, const char *name,
160 char find, const char *replace)
162 char *escaped = NULL;
167 namecopy = apr_pstrdup(pool, name);
169 p = strchr(namecopy, find);
170 if (!p) return namecopy;
175 /* terminate previous segment */
178 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
180 escaped = apr_pstrcat(pool, n, replace, NULL);
182 /* move to next segment */
186 /* append last segment if any */
188 escaped = apr_pstrcat(pool, escaped, n, NULL);
194 static void mag_store_deleg_creds(request_rec *req,
195 char *dir, char *clientname,
196 gss_cred_id_t delegated_cred,
199 gss_key_value_element_desc element;
200 gss_key_value_set_desc store;
205 /* We need to escape away '/', we can't have path separators in
206 * a ccache file name */
207 /* first double escape the esacping char (~) if any */
208 escaped = escape(req->pool, clientname, '~', "~~");
209 if (!escaped) return;
210 /* then escape away the separator (/) if any */
211 escaped = escape(req->pool, escaped, '/', "~");
212 if (!escaped) return;
214 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
216 element.key = "ccache";
217 element.value = value;
218 store.elements = &element;
221 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
222 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
223 if (GSS_ERROR(maj)) {
224 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
225 mag_error(req, "failed to store delegated creds",
233 static bool parse_auth_header(apr_pool_t *pool, const char **auth_header,
236 char *auth_header_value;
238 auth_header_value = ap_getword_white(pool, auth_header);
239 if (!auth_header_value) return false;
240 value->length = apr_base64_decode_len(auth_header_value) + 1;
241 value->value = apr_pcalloc(pool, value->length);
242 if (!value->value) return false;
243 value->length = apr_base64_decode(value->value, auth_header_value);
248 static bool is_mech_allowed(struct mag_config *cfg, gss_const_OID mech)
250 if (cfg->allowed_mechs == GSS_C_NO_OID_SET) return true;
252 for (int i = 0; i < cfg->allowed_mechs->count; i++) {
253 if (gss_oid_equal(&cfg->allowed_mechs->elements[i], mech)) {
260 #define AUTH_TYPE_NEGOTIATE 0
261 #define AUTH_TYPE_BASIC 1
262 #define AUTH_TYPE_RAW_NTLM 2
263 const char *auth_types[] = {
270 static int mag_auth(request_rec *req)
274 struct mag_config *cfg;
275 const char *auth_header;
276 char *auth_header_type;
277 int ret = HTTP_UNAUTHORIZED;
278 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
280 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
281 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
282 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
283 gss_buffer_desc ba_user;
284 gss_buffer_desc ba_pwd;
285 gss_name_t client = GSS_C_NO_NAME;
286 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
287 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
288 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
289 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
290 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
297 gss_OID mech_type = GSS_C_NO_OID;
298 gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
299 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
300 struct mag_conn *mc = NULL;
301 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
302 gss_name_t server = GSS_C_NO_NAME;
303 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
304 const char *user_ccache = NULL;
305 const char *orig_ccache = NULL;
307 uint32_t init_flags = 0;
311 type = ap_auth_type(req);
312 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
316 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
318 if (!cfg->allowed_mechs) {
319 /* Try to fetch the default set if not explicitly configured */
320 (void)mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
321 &server_cred, &cfg->allowed_mechs);
322 (void)gss_release_cred(&min, &server_cred);
325 /* implicit auth for subrequests if main auth already happened */
326 if (!ap_is_initial_req(req) && req->main != NULL) {
327 type = ap_auth_type(req->main);
328 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
329 /* warn if the subrequest location and the main request
330 * location have different configs */
331 if (cfg != ap_get_module_config(req->main->per_dir_config,
332 &auth_gssapi_module)) {
333 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
334 req, "Subrequest authentication bypass on "
335 "location with different configuration!");
337 if (req->main->user) {
338 req->user = apr_pstrdup(req->pool, req->main->user);
341 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
342 "The main request is tasked to establish the "
343 "security context, can't proceed!");
344 return HTTP_UNAUTHORIZED;
347 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
348 "Subrequest GSSAPI auth with no auth on the main "
349 "request. This operation may fail if other "
350 "subrequests already established a context or the "
351 "mechanism requires multiple roundtrips.");
356 if (!mag_conn_is_https(req->connection)) {
357 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
358 "Not a TLS connection, refusing to authenticate!");
363 if (cfg->gss_conn_ctx) {
364 mc = (struct mag_conn *)ap_get_module_config(
365 req->connection->conn_config,
366 &auth_gssapi_module);
368 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
369 "Failed to retrieve connection context!");
374 /* if available, session always supersedes connection bound data */
375 if (cfg->use_sessions) {
376 mag_check_session(req, cfg, &mc);
380 /* register the context in the memory pool, so it can be freed
381 * when the connection/request is terminated */
382 apr_pool_userdata_set(mc, "mag_conn_ptr",
383 mag_conn_destroy, mc->parent);
385 if (mc->established) {
386 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
387 "Already established context found!");
388 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
389 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
390 apr_psprintf(req->pool,
391 "%ld", (long)mc->expiration));
392 req->ap_auth_type = apr_pstrdup(req->pool,
393 auth_types[mc->auth_type]);
394 req->user = apr_pstrdup(req->pool, mc->user_name);
403 auth_header = apr_table_get(req->headers_in, "Authorization");
404 if (!auth_header) goto done;
406 auth_header_type = ap_getword_white(req->pool, &auth_header);
407 if (!auth_header_type) goto done;
409 for (i = 0; auth_types[i] != NULL; i++) {
410 if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
417 case AUTH_TYPE_NEGOTIATE:
418 if (!parse_auth_header(req->pool, &auth_header, &input)) {
422 case AUTH_TYPE_BASIC:
423 if (!cfg->use_basic_auth) {
427 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
428 if (!ba_pwd.value) goto done;
429 ba_user.value = ap_getword_nulls_nc(req->pool,
430 (char **)&ba_pwd.value, ':');
431 if (!ba_user.value) goto done;
432 if (((char *)ba_user.value)[0] == '\0' ||
433 ((char *)ba_pwd.value)[0] == '\0') {
434 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
435 "Invalid empty user or password for Basic Auth");
438 ba_user.length = strlen(ba_user.value);
439 ba_pwd.length = strlen(ba_pwd.value);
440 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
441 if (GSS_ERROR(maj)) {
442 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
444 mag_error(req, "gss_import_name() failed",
448 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
449 /* Set a per-thread ccache in case we are using kerberos,
450 * it is not elegant but avoids interference between threads */
451 long long unsigned int rndname;
453 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
454 sizeof(long long unsigned int));
455 if (rs != APR_SUCCESS) {
456 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
457 "Failed to generate random ccache name");
460 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
461 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
462 if (GSS_ERROR(maj)) {
463 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
465 mag_error(req, "gss_krb5_ccache_name() "
466 "failed", maj, min));
470 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
474 &user_cred, NULL, NULL);
475 if (GSS_ERROR(maj)) {
476 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
478 mag_error(req, "gss_acquire_cred_with_password() "
479 "failed", maj, min));
482 gss_release_name(&min, &client);
485 case AUTH_TYPE_RAW_NTLM:
486 if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
487 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
488 "NTLM Authentication is not allowed!");
492 if (!parse_auth_header(req->pool, &auth_header, &input)) {
496 desired_mechs = discard_const(&gss_mech_set_ntlmssp);
503 req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
505 #ifdef HAVE_CRED_STORE
506 if (cfg->use_s4u2proxy) {
507 cred_usage = GSS_C_BOTH;
510 if (!mag_acquire_creds(req, cfg, desired_mechs,
511 cred_usage, &acquired_cred, NULL)) {
515 if (auth_type == AUTH_TYPE_BASIC) {
516 if (cred_usage == GSS_C_BOTH) {
517 /* If GSS_C_BOTH is used then inquire_cred will return the client
518 * name instead of the SPN of the server credentials. Therefore we
519 * need to acquire a different set of credential setting
520 * GSS_C_ACCEPT explicitly */
521 if (!mag_acquire_creds(req, cfg, cfg->allowed_mechs,
522 GSS_C_ACCEPT, &server_cred, NULL)) {
526 server_cred = acquired_cred;
528 maj = gss_inquire_cred(&min, server_cred, &server,
530 if (GSS_ERROR(maj)) {
531 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
532 "%s", mag_error(req, "gss_inquired_cred_() "
533 "failed", maj, min));
536 if (server_cred != acquired_cred) {
537 gss_release_cred(&min, &server_cred);
540 #ifdef HAVE_CRED_STORE
541 if (cfg->deleg_ccache_dir) {
542 /* delegate ourselves credentials so we store them as requested */
543 init_flags |= GSS_C_DELEG_FLAG;
547 /* output and input are inverted here, this is intentional */
548 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
549 GSS_C_NO_OID, init_flags, 300,
550 GSS_C_NO_CHANNEL_BINDINGS, &output,
551 NULL, &input, NULL, NULL);
552 if (GSS_ERROR(maj)) {
553 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
554 "%s", mag_error(req, "gss_init_sec_context() "
555 "failed", maj, min));
560 if (auth_type == AUTH_TYPE_NEGOTIATE &&
561 cfg->allowed_mechs != GSS_C_NO_OID_SET) {
562 maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
563 if (GSS_ERROR(maj)) {
564 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
565 mag_error(req, "gss_set_neg_mechs() failed",
571 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
572 &input, GSS_C_NO_CHANNEL_BINDINGS,
573 &client, &mech_type, &output, &flags, &vtime,
575 if (GSS_ERROR(maj)) {
576 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
577 mag_error(req, "gss_accept_sec_context() failed",
581 if (auth_type == AUTH_TYPE_BASIC) {
583 apr_pool_cleanup_run(mc->parent, mc, mag_conn_destroy);
586 while (maj == GSS_S_CONTINUE_NEEDED) {
587 gss_release_buffer(&min, &input);
588 /* output and input are inverted here, this is intentional */
589 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
590 GSS_C_NO_OID, init_flags, 300,
591 GSS_C_NO_CHANNEL_BINDINGS, &output,
592 NULL, &input, NULL, NULL);
593 if (GSS_ERROR(maj)) {
594 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
595 "%s", mag_error(req, "gss_init_sec_context() "
596 "failed", maj, min));
599 gss_release_buffer(&min, &output);
600 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
601 &input, GSS_C_NO_CHANNEL_BINDINGS,
602 &client, &mech_type, &output, &flags,
603 &vtime, &delegated_cred);
604 if (GSS_ERROR(maj)) {
605 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
606 "%s", mag_error(req, "gss_accept_sec_context()"
607 " failed", maj, min));
611 } else if (maj == GSS_S_CONTINUE_NEEDED) {
613 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
614 "Mechanism needs continuation but neither "
615 "GssapiConnectionBound nor "
616 "GssapiUseSessions are available");
617 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
618 gss_release_buffer(&min, &output);
621 /* auth not complete send token and wait next packet */
625 /* Always set the GSS name in an env var */
626 maj = gss_display_name(&min, client, &name, NULL);
627 if (GSS_ERROR(maj)) {
628 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
629 mag_error(req, "gss_display_name() failed",
633 clientname = apr_pstrndup(req->pool, name.value, name.length);
634 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
635 expiration = time(NULL) + vtime;
636 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
637 apr_psprintf(req->pool, "%ld", (long)expiration));
639 #ifdef HAVE_CRED_STORE
640 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
641 char *ccachefile = NULL;
643 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
644 delegated_cred, &ccachefile);
647 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
652 if (cfg->map_to_local) {
653 maj = gss_localname(&min, client, mech_type, &lname);
654 if (maj != GSS_S_COMPLETE) {
655 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
656 mag_error(req, "gss_localname() failed", maj, min));
659 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
661 req->user = clientname;
665 mc->user_name = apr_pstrdup(mc->parent, req->user);
666 mc->gss_name = apr_pstrdup(mc->parent, clientname);
667 mc->established = true;
668 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
669 vtime = MIN_SESS_EXP_TIME;
671 mc->expiration = expiration;
672 if (cfg->use_sessions) {
673 mag_attempt_session(req, cfg, mc);
675 mc->auth_type = auth_type;
678 if (cfg->send_persist)
679 apr_table_set(req->headers_out, "Persistent-Auth",
680 cfg->gss_conn_ctx ? "true" : "false");
685 if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
686 int prefixlen = strlen(auth_types[auth_type]) + 1;
687 replen = apr_base64_encode_len(output.length) + 1;
688 reply = apr_pcalloc(req->pool, prefixlen + replen);
690 memcpy(reply, auth_types[auth_type], prefixlen - 1);
691 reply[prefixlen - 1] = ' ';
692 apr_base64_encode(&reply[prefixlen], output.value, output.length);
693 apr_table_add(req->err_headers_out,
694 "WWW-Authenticate", reply);
696 } else if (ret == HTTP_UNAUTHORIZED) {
697 apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
698 if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
699 apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
701 if (cfg->use_basic_auth) {
702 apr_table_add(req->err_headers_out,
704 apr_psprintf(req->pool, "Basic realm=\"%s\"",
708 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
709 if (user_ccache != NULL) {
710 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
711 if (maj != GSS_S_COMPLETE) {
712 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
713 "Failed to restore per-thread ccache, %s",
714 mag_error(req, "gss_krb5_ccache_name() "
715 "failed", maj, min));
719 gss_delete_sec_context(&min, &user_ctx, &output);
720 gss_release_cred(&min, &user_cred);
721 gss_release_cred(&min, &acquired_cred);
722 gss_release_cred(&min, &delegated_cred);
723 gss_release_buffer(&min, &output);
724 gss_release_name(&min, &client);
725 gss_release_name(&min, &server);
726 gss_release_buffer(&min, &name);
727 gss_release_buffer(&min, &lname);
732 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
734 struct mag_config *cfg;
736 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
742 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
744 struct mag_config *cfg = (struct mag_config *)mconfig;
745 cfg->ssl_only = on ? true : false;
749 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
751 struct mag_config *cfg = (struct mag_config *)mconfig;
752 cfg->map_to_local = on ? true : false;
756 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
758 struct mag_config *cfg = (struct mag_config *)mconfig;
759 cfg->gss_conn_ctx = on ? true : false;
763 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
765 struct mag_config *cfg = (struct mag_config *)mconfig;
766 cfg->send_persist = on ? true : false;
770 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
772 struct mag_config *cfg = (struct mag_config *)mconfig;
773 cfg->use_sessions = on ? true : false;
777 #ifdef HAVE_CRED_STORE
778 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
780 struct mag_config *cfg = (struct mag_config *)mconfig;
781 cfg->use_s4u2proxy = on ? true : false;
783 if (cfg->deleg_ccache_dir == NULL) {
784 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
790 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
792 struct mag_config *cfg = (struct mag_config *)mconfig;
799 if (strncmp(w, "key:", 4) != 0) {
800 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
801 "Invalid key format, expected prefix 'key:'");
806 l = apr_base64_decode_len(k);
807 val = apr_palloc(parms->temp_pool, l);
809 keys.length = (int)apr_base64_decode_binary(val, k);
810 keys.value = (unsigned char *)val;
812 if (keys.length != 32) {
813 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
814 "Invalid key length, expected 32 got %d", keys.length);
818 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
820 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
821 "Failed to import sealing key!");
826 #ifdef HAVE_CRED_STORE
828 #define MAX_CRED_OPTIONS 10
830 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
833 struct mag_config *cfg = (struct mag_config *)mconfig;
834 gss_key_value_element_desc *elements;
843 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
844 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
848 key = apr_pstrndup(parms->pool, w, (p-w));
849 value = apr_pstrdup(parms->pool, p + 1);
851 if (!cfg->cred_store) {
852 cfg->cred_store = apr_pcalloc(parms->pool,
853 sizeof(gss_key_value_set_desc));
854 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
855 cfg->cred_store->elements = apr_palloc(parms->pool, size);
858 elements = cfg->cred_store->elements;
859 count = cfg->cred_store->count;
861 if (count >= MAX_CRED_OPTIONS) {
862 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
863 "Too many GssapiCredStore options (MAX: %d)",
867 cfg->cred_store->count++;
869 elements[count].key = key;
870 elements[count].value = value;
875 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
878 struct mag_config *cfg = (struct mag_config *)mconfig;
880 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
886 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
888 struct mag_config *cfg = (struct mag_config *)mconfig;
890 cfg->use_basic_auth = on ? true : false;
894 #define MAX_ALLOWED_MECHS 10
896 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
899 struct mag_config *cfg = (struct mag_config *)mconfig;
903 if (!cfg->allowed_mechs) {
904 cfg->allowed_mechs = apr_pcalloc(parms->pool,
905 sizeof(gss_OID_set_desc));
906 size = sizeof(gss_OID) * MAX_ALLOWED_MECHS;
907 cfg->allowed_mechs->elements = apr_palloc(parms->pool, size);
910 if (strcmp(w, "krb5") == 0) {
912 } else if (strcmp(w, "iakerb") == 0) {
913 oid = gss_mech_iakerb;
914 } else if (strcmp(w, "ntlmssp") == 0) {
915 oid = &gss_mech_ntlmssp;
917 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
918 "Unrecognized GSSAPI Mechanism: %s", w);
922 if (cfg->allowed_mechs->count >= MAX_ALLOWED_MECHS) {
923 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
924 "Too many GssapiAllowedMech options (MAX: %d)",
928 cfg->allowed_mechs->elements[cfg->allowed_mechs->count] = *oid;
929 cfg->allowed_mechs->count++;
934 static const command_rec mag_commands[] = {
935 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
936 "Work only if connection is SSL Secured"),
937 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
938 "Translate principals to local names"),
939 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
940 "Authentication is bound to the TCP connection"),
941 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
942 "Send Persitent-Auth header according to connection bound"),
943 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
944 "Authentication uses mod_sessions to hold status"),
945 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
946 "Key Used to seal session data."),
947 #ifdef HAVE_CRED_STORE
948 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
949 "Initializes credentials for s4u2proxy usage"),
950 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
952 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
953 OR_AUTHCFG, "Directory to store delegated credentials"),
955 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
956 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
957 "Allows use of Basic Auth for authentication"),
959 AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
960 "Allowed Mechanisms"),
965 mag_register_hooks(apr_pool_t *p)
967 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
968 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
969 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
972 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
974 STANDARD20_MODULE_STUFF,
975 mag_create_dir_config,