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 char *mag_gss_name_to_ccache_name(request_rec *req,
195 char *dir, const char *gss_name)
199 /* We need to escape away '/', we can't have path separators in
200 * a ccache file name */
201 /* first double escape the esacping char (~) if any */
202 escaped = escape(req->pool, gss_name, '~', "~~");
203 /* then escape away the separator (/) if any */
204 escaped = escape(req->pool, escaped, '/', "~");
206 return apr_psprintf(req->pool, "%s/%s", dir, escaped);
209 static void mag_set_KRB5CCANME(request_rec *req, char *ccname)
215 status = apr_stat(&finfo, ccname, APR_FINFO_MIN, req->pool);
216 if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
217 /* set the file cache anyway, but warn */
218 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
219 "KRB5CCNAME file (%s) lookup failed!", ccname);
222 value = apr_psprintf(req->pool, "FILE:%s", ccname);
223 apr_table_set(req->subprocess_env, "KRB5CCNAME", value);
226 static void mag_store_deleg_creds(request_rec *req,
227 char *dir, char *clientname,
228 gss_cred_id_t delegated_cred,
231 gss_key_value_element_desc element;
232 gss_key_value_set_desc store;
235 element.key = "ccache";
236 store.elements = &element;
239 ccname = mag_gss_name_to_ccache_name(req, dir, clientname);
240 element.value = apr_psprintf(req->pool, "FILE:%s", ccname);
242 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
243 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
244 if (GSS_ERROR(maj)) {
245 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
246 mag_error(req, "failed to store delegated creds",
250 *ccachefile = ccname;
254 static bool parse_auth_header(apr_pool_t *pool, const char **auth_header,
257 char *auth_header_value;
259 auth_header_value = ap_getword_white(pool, auth_header);
260 if (!auth_header_value) return false;
261 value->length = apr_base64_decode_len(auth_header_value) + 1;
262 value->value = apr_pcalloc(pool, value->length);
263 if (!value->value) return false;
264 value->length = apr_base64_decode(value->value, auth_header_value);
269 static bool is_mech_allowed(struct mag_config *cfg, gss_const_OID mech)
271 if (cfg->allowed_mechs == GSS_C_NO_OID_SET) return true;
273 for (int i = 0; i < cfg->allowed_mechs->count; i++) {
274 if (gss_oid_equal(&cfg->allowed_mechs->elements[i], mech)) {
281 #define AUTH_TYPE_NEGOTIATE 0
282 #define AUTH_TYPE_BASIC 1
283 #define AUTH_TYPE_RAW_NTLM 2
284 const char *auth_types[] = {
291 static int mag_auth(request_rec *req)
295 struct mag_config *cfg;
296 const char *auth_header;
297 char *auth_header_type;
298 int ret = HTTP_UNAUTHORIZED;
299 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
301 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
302 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
303 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
304 gss_buffer_desc ba_user;
305 gss_buffer_desc ba_pwd;
306 gss_name_t client = GSS_C_NO_NAME;
307 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
308 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
309 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
310 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
311 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
318 gss_OID mech_type = GSS_C_NO_OID;
319 gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
320 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
321 struct mag_conn *mc = NULL;
322 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
323 gss_name_t server = GSS_C_NO_NAME;
324 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
325 const char *user_ccache = NULL;
326 const char *orig_ccache = NULL;
328 uint32_t init_flags = 0;
332 type = ap_auth_type(req);
333 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
337 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
339 if (!cfg->allowed_mechs) {
340 /* Try to fetch the default set if not explicitly configured */
341 (void)mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
342 &server_cred, &cfg->allowed_mechs);
343 (void)gss_release_cred(&min, &server_cred);
346 /* implicit auth for subrequests if main auth already happened */
347 if (!ap_is_initial_req(req) && req->main != NULL) {
348 type = ap_auth_type(req->main);
349 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
350 /* warn if the subrequest location and the main request
351 * location have different configs */
352 if (cfg != ap_get_module_config(req->main->per_dir_config,
353 &auth_gssapi_module)) {
354 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
355 req, "Subrequest authentication bypass on "
356 "location with different configuration!");
358 if (req->main->user) {
359 req->user = apr_pstrdup(req->pool, req->main->user);
362 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
363 "The main request is tasked to establish the "
364 "security context, can't proceed!");
365 return HTTP_UNAUTHORIZED;
368 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
369 "Subrequest GSSAPI auth with no auth on the main "
370 "request. This operation may fail if other "
371 "subrequests already established a context or the "
372 "mechanism requires multiple roundtrips.");
377 if (!mag_conn_is_https(req->connection)) {
378 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
379 "Not a TLS connection, refusing to authenticate!");
384 if (cfg->gss_conn_ctx) {
385 mc = (struct mag_conn *)ap_get_module_config(
386 req->connection->conn_config,
387 &auth_gssapi_module);
389 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
390 "Failed to retrieve connection context!");
395 /* if available, session always supersedes connection bound data */
396 if (cfg->use_sessions) {
397 mag_check_session(req, cfg, &mc);
401 /* register the context in the memory pool, so it can be freed
402 * when the connection/request is terminated */
403 apr_pool_userdata_set(mc, "mag_conn_ptr",
404 mag_conn_destroy, mc->parent);
406 if (mc->established) {
407 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
408 "Already established context found!");
409 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
410 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
411 apr_psprintf(req->pool,
412 "%ld", (long)mc->expiration));
413 req->ap_auth_type = apr_pstrdup(req->pool,
414 auth_types[mc->auth_type]);
415 req->user = apr_pstrdup(req->pool, mc->user_name);
416 if (cfg->deleg_ccache_dir && mc->delegated) {
418 ccname = mag_gss_name_to_ccache_name(req,
419 cfg->deleg_ccache_dir,
422 mag_set_KRB5CCANME(req, ccname);
425 if (mc->auth_type != AUTH_TYPE_BASIC) {
426 /* In case we have basic auth, we need to check if the session
427 * matches the credentials that have been sent */
437 auth_header = apr_table_get(req->headers_in, "Authorization");
438 if (!auth_header) goto done;
440 auth_header_type = ap_getword_white(req->pool, &auth_header);
441 if (!auth_header_type) goto done;
443 for (i = 0; auth_types[i] != NULL; i++) {
444 if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
451 case AUTH_TYPE_NEGOTIATE:
452 if (!parse_auth_header(req->pool, &auth_header, &input)) {
456 case AUTH_TYPE_BASIC:
457 if (!cfg->use_basic_auth) {
461 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
462 if (!ba_pwd.value) goto done;
463 ba_user.value = ap_getword_nulls_nc(req->pool,
464 (char **)&ba_pwd.value, ':');
465 if (!ba_user.value) goto done;
466 if (((char *)ba_user.value)[0] == '\0' ||
467 ((char *)ba_pwd.value)[0] == '\0') {
468 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
469 "Invalid empty user or password for Basic Auth");
472 ba_user.length = strlen(ba_user.value);
473 ba_pwd.length = strlen(ba_pwd.value);
475 if (mc && mag_basic_check(cfg, mc, ba_user, ba_pwd)) {
480 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
481 if (GSS_ERROR(maj)) {
482 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
484 mag_error(req, "gss_import_name() failed",
488 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
489 /* Set a per-thread ccache in case we are using kerberos,
490 * it is not elegant but avoids interference between threads */
491 long long unsigned int rndname;
493 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
494 sizeof(long long unsigned int));
495 if (rs != APR_SUCCESS) {
496 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
497 "Failed to generate random ccache name");
500 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
501 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
502 if (GSS_ERROR(maj)) {
503 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
505 mag_error(req, "gss_krb5_ccache_name() "
506 "failed", maj, min));
510 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
514 &user_cred, NULL, NULL);
515 if (GSS_ERROR(maj)) {
516 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
518 mag_error(req, "gss_acquire_cred_with_password() "
519 "failed", maj, min));
522 gss_release_name(&min, &client);
525 case AUTH_TYPE_RAW_NTLM:
526 if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
527 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
528 "NTLM Authentication is not allowed!");
532 if (!parse_auth_header(req->pool, &auth_header, &input)) {
536 desired_mechs = discard_const(&gss_mech_set_ntlmssp);
543 req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
545 #ifdef HAVE_CRED_STORE
546 if (cfg->use_s4u2proxy) {
547 cred_usage = GSS_C_BOTH;
550 if (!mag_acquire_creds(req, cfg, desired_mechs,
551 cred_usage, &acquired_cred, NULL)) {
555 if (auth_type == AUTH_TYPE_BASIC) {
556 if (cred_usage == GSS_C_BOTH) {
557 /* If GSS_C_BOTH is used then inquire_cred will return the client
558 * name instead of the SPN of the server credentials. Therefore we
559 * need to acquire a different set of credential setting
560 * GSS_C_ACCEPT explicitly */
561 if (!mag_acquire_creds(req, cfg, cfg->allowed_mechs,
562 GSS_C_ACCEPT, &server_cred, NULL)) {
566 server_cred = acquired_cred;
568 maj = gss_inquire_cred(&min, server_cred, &server,
570 if (GSS_ERROR(maj)) {
571 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
572 "%s", mag_error(req, "gss_inquired_cred_() "
573 "failed", maj, min));
576 if (server_cred != acquired_cred) {
577 gss_release_cred(&min, &server_cred);
580 #ifdef HAVE_CRED_STORE
581 if (cfg->deleg_ccache_dir) {
582 /* delegate ourselves credentials so we store them as requested */
583 init_flags |= GSS_C_DELEG_FLAG;
587 /* output and input are inverted here, this is intentional */
588 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
589 GSS_C_NO_OID, init_flags, 300,
590 GSS_C_NO_CHANNEL_BINDINGS, &output,
591 NULL, &input, NULL, NULL);
592 if (GSS_ERROR(maj)) {
593 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
594 "%s", mag_error(req, "gss_init_sec_context() "
595 "failed", maj, min));
600 if (auth_type == AUTH_TYPE_NEGOTIATE &&
601 cfg->allowed_mechs != GSS_C_NO_OID_SET) {
602 maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
603 if (GSS_ERROR(maj)) {
604 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
605 mag_error(req, "gss_set_neg_mechs() failed",
611 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
612 &input, GSS_C_NO_CHANNEL_BINDINGS,
613 &client, &mech_type, &output, &flags, &vtime,
615 if (GSS_ERROR(maj)) {
616 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
617 mag_error(req, "gss_accept_sec_context() failed",
621 if (auth_type == AUTH_TYPE_BASIC) {
622 while (maj == GSS_S_CONTINUE_NEEDED) {
623 gss_release_buffer(&min, &input);
624 /* output and input are inverted here, this is intentional */
625 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
626 GSS_C_NO_OID, init_flags, 300,
627 GSS_C_NO_CHANNEL_BINDINGS, &output,
628 NULL, &input, NULL, NULL);
629 if (GSS_ERROR(maj)) {
630 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
631 "%s", mag_error(req, "gss_init_sec_context() "
632 "failed", maj, min));
635 gss_release_buffer(&min, &output);
636 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
637 &input, GSS_C_NO_CHANNEL_BINDINGS,
638 &client, &mech_type, &output, &flags,
639 &vtime, &delegated_cred);
640 if (GSS_ERROR(maj)) {
641 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
642 "%s", mag_error(req, "gss_accept_sec_context()"
643 " failed", maj, min));
647 } else if (maj == GSS_S_CONTINUE_NEEDED) {
649 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
650 "Mechanism needs continuation but neither "
651 "GssapiConnectionBound nor "
652 "GssapiUseSessions are available");
653 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
654 gss_release_buffer(&min, &output);
657 /* auth not complete send token and wait next packet */
661 /* Always set the GSS name in an env var */
662 maj = gss_display_name(&min, client, &name, NULL);
663 if (GSS_ERROR(maj)) {
664 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
665 mag_error(req, "gss_display_name() failed",
669 clientname = apr_pstrndup(req->pool, name.value, name.length);
670 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
671 expiration = time(NULL) + vtime;
672 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
673 apr_psprintf(req->pool, "%ld", (long)expiration));
675 #ifdef HAVE_CRED_STORE
676 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
677 char *ccachefile = NULL;
679 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
680 delegated_cred, &ccachefile);
683 mag_set_KRB5CCANME(req, ccachefile);
687 mc->delegated = true;
692 if (cfg->map_to_local) {
693 maj = gss_localname(&min, client, mech_type, &lname);
694 if (maj != GSS_S_COMPLETE) {
695 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
696 mag_error(req, "gss_localname() failed", maj, min));
699 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
701 req->user = clientname;
705 mc->user_name = apr_pstrdup(mc->parent, req->user);
706 mc->gss_name = apr_pstrdup(mc->parent, clientname);
707 mc->established = true;
708 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
709 vtime = MIN_SESS_EXP_TIME;
711 mc->expiration = expiration;
712 mc->auth_type = auth_type;
713 if (auth_type == AUTH_TYPE_BASIC) {
714 mag_basic_cache(cfg, mc, ba_user, ba_pwd);
716 if (cfg->use_sessions) {
717 mag_attempt_session(req, cfg, mc);
721 if (cfg->send_persist)
722 apr_table_set(req->headers_out, "Persistent-Auth",
723 cfg->gss_conn_ctx ? "true" : "false");
728 if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
729 int prefixlen = strlen(auth_types[auth_type]) + 1;
730 replen = apr_base64_encode_len(output.length) + 1;
731 reply = apr_pcalloc(req->pool, prefixlen + replen);
733 memcpy(reply, auth_types[auth_type], prefixlen - 1);
734 reply[prefixlen - 1] = ' ';
735 apr_base64_encode(&reply[prefixlen], output.value, output.length);
736 apr_table_add(req->err_headers_out,
737 "WWW-Authenticate", reply);
739 } else if (ret == HTTP_UNAUTHORIZED) {
740 apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
741 if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
742 apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
744 if (cfg->use_basic_auth) {
745 apr_table_add(req->err_headers_out,
747 apr_psprintf(req->pool, "Basic realm=\"%s\"",
751 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
752 if (user_ccache != NULL) {
753 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
754 if (maj != GSS_S_COMPLETE) {
755 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
756 "Failed to restore per-thread ccache, %s",
757 mag_error(req, "gss_krb5_ccache_name() "
758 "failed", maj, min));
762 gss_delete_sec_context(&min, &user_ctx, &output);
763 gss_release_cred(&min, &user_cred);
764 gss_release_cred(&min, &acquired_cred);
765 gss_release_cred(&min, &delegated_cred);
766 gss_release_buffer(&min, &output);
767 gss_release_name(&min, &client);
768 gss_release_name(&min, &server);
769 gss_release_buffer(&min, &name);
770 gss_release_buffer(&min, &lname);
775 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
777 struct mag_config *cfg;
779 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
785 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
787 struct mag_config *cfg = (struct mag_config *)mconfig;
788 cfg->ssl_only = on ? true : false;
792 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
794 struct mag_config *cfg = (struct mag_config *)mconfig;
795 cfg->map_to_local = on ? true : false;
799 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
801 struct mag_config *cfg = (struct mag_config *)mconfig;
802 cfg->gss_conn_ctx = on ? true : false;
806 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
808 struct mag_config *cfg = (struct mag_config *)mconfig;
809 cfg->send_persist = on ? true : false;
813 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
815 struct mag_config *cfg = (struct mag_config *)mconfig;
816 cfg->use_sessions = on ? true : false;
820 #ifdef HAVE_CRED_STORE
821 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
823 struct mag_config *cfg = (struct mag_config *)mconfig;
824 cfg->use_s4u2proxy = on ? true : false;
826 if (cfg->deleg_ccache_dir == NULL) {
827 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
833 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
835 struct mag_config *cfg = (struct mag_config *)mconfig;
842 if (strncmp(w, "key:", 4) != 0) {
843 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
844 "Invalid key format, expected prefix 'key:'");
849 l = apr_base64_decode_len(k);
850 val = apr_palloc(parms->temp_pool, l);
852 keys.length = (int)apr_base64_decode_binary(val, k);
853 keys.value = (unsigned char *)val;
855 if (keys.length != 32) {
856 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
857 "Invalid key length, expected 32 got %d", keys.length);
861 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
863 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
864 "Failed to import sealing key!");
869 #ifdef HAVE_CRED_STORE
871 #define MAX_CRED_OPTIONS 10
873 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
876 struct mag_config *cfg = (struct mag_config *)mconfig;
877 gss_key_value_element_desc *elements;
886 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
887 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
891 key = apr_pstrndup(parms->pool, w, (p-w));
892 value = apr_pstrdup(parms->pool, p + 1);
894 if (!cfg->cred_store) {
895 cfg->cred_store = apr_pcalloc(parms->pool,
896 sizeof(gss_key_value_set_desc));
897 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
898 cfg->cred_store->elements = apr_palloc(parms->pool, size);
901 elements = cfg->cred_store->elements;
902 count = cfg->cred_store->count;
904 if (count >= MAX_CRED_OPTIONS) {
905 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
906 "Too many GssapiCredStore options (MAX: %d)",
910 cfg->cred_store->count++;
912 elements[count].key = key;
913 elements[count].value = value;
918 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
921 struct mag_config *cfg = (struct mag_config *)mconfig;
923 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
929 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
931 struct mag_config *cfg = (struct mag_config *)mconfig;
933 cfg->use_basic_auth = on ? true : false;
937 #define MAX_ALLOWED_MECHS 10
939 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
942 struct mag_config *cfg = (struct mag_config *)mconfig;
946 if (!cfg->allowed_mechs) {
947 cfg->allowed_mechs = apr_pcalloc(parms->pool,
948 sizeof(gss_OID_set_desc));
949 size = sizeof(gss_OID) * MAX_ALLOWED_MECHS;
950 cfg->allowed_mechs->elements = apr_palloc(parms->pool, size);
953 if (strcmp(w, "krb5") == 0) {
955 } else if (strcmp(w, "iakerb") == 0) {
956 oid = gss_mech_iakerb;
957 } else if (strcmp(w, "ntlmssp") == 0) {
958 oid = &gss_mech_ntlmssp;
960 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
961 "Unrecognized GSSAPI Mechanism: %s", w);
965 if (cfg->allowed_mechs->count >= MAX_ALLOWED_MECHS) {
966 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
967 "Too many GssapiAllowedMech options (MAX: %d)",
971 cfg->allowed_mechs->elements[cfg->allowed_mechs->count] = *oid;
972 cfg->allowed_mechs->count++;
977 static const command_rec mag_commands[] = {
978 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
979 "Work only if connection is SSL Secured"),
980 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
981 "Translate principals to local names"),
982 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
983 "Authentication is bound to the TCP connection"),
984 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
985 "Send Persitent-Auth header according to connection bound"),
986 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
987 "Authentication uses mod_sessions to hold status"),
988 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
989 "Key Used to seal session data."),
990 #ifdef HAVE_CRED_STORE
991 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
992 "Initializes credentials for s4u2proxy usage"),
993 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
995 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
996 OR_AUTHCFG, "Directory to store delegated credentials"),
998 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
999 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
1000 "Allows use of Basic Auth for authentication"),
1002 AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
1003 "Allowed Mechanisms"),
1008 mag_register_hooks(apr_pool_t *p)
1010 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1011 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1012 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1015 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1017 STANDARD20_MODULE_STUFF,
1018 mag_create_dir_config,