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 = mag_new_conn_ctx(c->pool);
100 ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
104 static apr_status_t mag_conn_destroy(void *ptr)
106 struct mag_conn *mc = (struct mag_conn *)ptr;
110 (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
115 struct mag_conn *mag_new_conn_ctx(apr_pool_t *pool)
119 mc = apr_pcalloc(pool, sizeof(struct mag_conn));
120 apr_pool_create(&mc->pool, pool);
121 /* register the context in the memory pool, so it can be freed
122 * when the connection/request is terminated */
123 apr_pool_cleanup_register(mc->pool, (void *)mc,
124 mag_conn_destroy, apr_pool_cleanup_null);
129 static void mag_conn_clear(struct mag_conn *mc)
131 (void)mag_conn_destroy(mc);
134 apr_pool_clear(mc->pool);
136 memset(mc, 0, sizeof(struct mag_conn));
140 static bool mag_conn_is_https(conn_rec *c)
143 if (mag_is_https(c)) return true;
149 static bool mag_acquire_creds(request_rec *req,
150 struct mag_config *cfg,
151 gss_OID_set desired_mechs,
152 gss_cred_usage_t cred_usage,
153 gss_cred_id_t *creds,
154 gss_OID_set *actual_mechs)
157 #ifdef HAVE_CRED_STORE
158 gss_const_key_value_set_t store = cfg->cred_store;
160 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
161 desired_mechs, cred_usage, store, creds,
164 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
165 desired_mechs, cred_usage, creds,
169 if (GSS_ERROR(maj)) {
170 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
171 mag_error(req, "gss_acquire_cred[_from]() "
172 "failed to get server creds",
180 #ifdef HAVE_CRED_STORE
181 static char *escape(apr_pool_t *pool, const char *name,
182 char find, const char *replace)
184 char *escaped = NULL;
189 namecopy = apr_pstrdup(pool, name);
191 p = strchr(namecopy, find);
192 if (!p) return namecopy;
197 /* terminate previous segment */
200 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
202 escaped = apr_pstrcat(pool, n, replace, NULL);
204 /* move to next segment */
208 /* append last segment if any */
210 escaped = apr_pstrcat(pool, escaped, n, NULL);
216 static char *mag_gss_name_to_ccache_name(request_rec *req,
217 char *dir, const char *gss_name)
221 /* We need to escape away '/', we can't have path separators in
222 * a ccache file name */
223 /* first double escape the esacping char (~) if any */
224 escaped = escape(req->pool, gss_name, '~', "~~");
225 /* then escape away the separator (/) if any */
226 escaped = escape(req->pool, escaped, '/', "~");
228 return apr_psprintf(req->pool, "%s/%s", dir, escaped);
231 static void mag_set_KRB5CCANME(request_rec *req, char *ccname)
237 status = apr_stat(&finfo, ccname, APR_FINFO_MIN, req->pool);
238 if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
239 /* set the file cache anyway, but warn */
240 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
241 "KRB5CCNAME file (%s) lookup failed!", ccname);
244 value = apr_psprintf(req->pool, "FILE:%s", ccname);
245 apr_table_set(req->subprocess_env, "KRB5CCNAME", value);
248 static void mag_store_deleg_creds(request_rec *req,
249 char *dir, char *clientname,
250 gss_cred_id_t delegated_cred,
253 gss_key_value_element_desc element;
254 gss_key_value_set_desc store;
257 element.key = "ccache";
258 store.elements = &element;
261 ccname = mag_gss_name_to_ccache_name(req, dir, clientname);
262 element.value = apr_psprintf(req->pool, "FILE:%s", ccname);
264 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
265 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
266 if (GSS_ERROR(maj)) {
267 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
268 mag_error(req, "failed to store delegated creds",
272 *ccachefile = ccname;
276 static bool parse_auth_header(apr_pool_t *pool, const char **auth_header,
279 char *auth_header_value;
281 auth_header_value = ap_getword_white(pool, auth_header);
282 if (!auth_header_value) return false;
283 value->length = apr_base64_decode_len(auth_header_value) + 1;
284 value->value = apr_pcalloc(pool, value->length);
285 if (!value->value) return false;
286 value->length = apr_base64_decode(value->value, auth_header_value);
291 static bool is_mech_allowed(struct mag_config *cfg, gss_const_OID mech)
293 if (cfg->allowed_mechs == GSS_C_NO_OID_SET) return true;
295 for (int i = 0; i < cfg->allowed_mechs->count; i++) {
296 if (gss_oid_equal(&cfg->allowed_mechs->elements[i], mech)) {
303 #define AUTH_TYPE_NEGOTIATE 0
304 #define AUTH_TYPE_BASIC 1
305 #define AUTH_TYPE_RAW_NTLM 2
306 const char *auth_types[] = {
313 static void mag_set_req_data(request_rec *req,
314 struct mag_config *cfg,
317 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
318 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
319 apr_psprintf(req->pool,
320 "%ld", (long)mc->expiration));
321 req->ap_auth_type = apr_pstrdup(req->pool,
322 auth_types[mc->auth_type]);
323 req->user = apr_pstrdup(req->pool, mc->user_name);
324 if (cfg->deleg_ccache_dir && mc->delegated) {
326 ccname = mag_gss_name_to_ccache_name(req,
327 cfg->deleg_ccache_dir,
330 mag_set_KRB5CCANME(req, ccname);
335 static bool mag_auth_basic(request_rec *req,
336 struct mag_config *cfg,
337 gss_buffer_desc ba_user,
338 gss_buffer_desc ba_pwd,
339 gss_cred_id_t acquired_cred,
340 gss_cred_usage_t cred_usage,
343 gss_cred_id_t *delegated_cred,
346 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
347 const char *user_ccache = NULL;
348 const char *orig_ccache = NULL;
349 long long unsigned int rndname;
352 gss_name_t user = GSS_C_NO_NAME;
353 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
354 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
355 gss_name_t server = GSS_C_NO_NAME;
356 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
357 gss_ctx_id_t server_ctx = GSS_C_NO_CONTEXT;
358 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
359 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
360 uint32_t init_flags = 0;
364 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
365 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
366 sizeof(long long unsigned int));
367 if (rs != APR_SUCCESS) {
368 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
369 "Failed to generate random ccache name");
372 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
373 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
374 if (GSS_ERROR(maj)) {
375 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
377 mag_error(req, "gss_krb5_ccache_name() "
378 "failed", maj, min));
383 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &user);
384 if (GSS_ERROR(maj)) {
385 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
387 mag_error(req, "gss_import_name() failed",
392 maj = gss_acquire_cred_with_password(&min, user, &ba_pwd,
396 &user_cred, NULL, NULL);
397 if (GSS_ERROR(maj)) {
398 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
400 mag_error(req, "gss_acquire_cred_with_password() "
401 "failed", maj, min));
405 if (cred_usage == GSS_C_BOTH) {
406 /* If GSS_C_BOTH is used then inquire_cred will return the client
407 * name instead of the SPN of the server credentials. Therefore we
408 * need to acquire a different set of credential setting
409 * GSS_C_ACCEPT explicitly */
410 if (!mag_acquire_creds(req, cfg, cfg->allowed_mechs,
411 GSS_C_ACCEPT, &server_cred, NULL)) {
415 server_cred = acquired_cred;
417 maj = gss_inquire_cred(&min, server_cred, &server,
419 if (GSS_ERROR(maj)) {
420 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
421 "%s", mag_error(req, "gss_inquired_cred_() "
422 "failed", maj, min));
426 #ifdef HAVE_CRED_STORE
427 if (cfg->deleg_ccache_dir) {
428 /* delegate ourselves credentials so we store them as requested */
429 init_flags |= GSS_C_DELEG_FLAG;
434 /* output and input are inverted here, this is intentional */
435 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
436 GSS_C_NO_OID, init_flags, 300,
437 GSS_C_NO_CHANNEL_BINDINGS, &output,
438 NULL, &input, NULL, NULL);
439 if (GSS_ERROR(maj)) {
440 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
441 "%s", mag_error(req, "gss_init_sec_context() "
442 "failed", maj, min));
445 gss_release_buffer(&min, &output);
446 maj = gss_accept_sec_context(&min, &server_ctx, acquired_cred,
447 &input, GSS_C_NO_CHANNEL_BINDINGS,
448 client, mech_type, &output, NULL,
449 vtime, delegated_cred);
450 if (GSS_ERROR(maj)) {
451 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
452 "%s", mag_error(req, "gss_accept_sec_context()"
453 " failed", maj, min));
456 gss_release_buffer(&min, &input);
457 } while (maj == GSS_S_CONTINUE_NEEDED);
462 gss_release_buffer(&min, &output);
463 gss_release_buffer(&min, &input);
464 gss_release_name(&min, &server);
465 if (server_cred != acquired_cred) {
466 gss_release_cred(&min, &server_cred);
468 gss_delete_sec_context(&min, &server_ctx, GSS_C_NO_BUFFER);
469 gss_release_name(&min, &user);
470 gss_release_cred(&min, &user_cred);
471 gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER);
472 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
473 if (user_ccache != NULL) {
474 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
475 if (maj != GSS_S_COMPLETE) {
476 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
477 "Failed to restore per-thread ccache, %s",
478 mag_error(req, "gss_krb5_ccache_name() "
479 "failed", maj, min));
487 static int mag_auth(request_rec *req)
491 struct mag_config *cfg;
492 const char *auth_header;
493 char *auth_header_type;
494 int ret = HTTP_UNAUTHORIZED;
495 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
497 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
498 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
499 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
500 gss_buffer_desc ba_user;
501 gss_buffer_desc ba_pwd;
502 gss_name_t client = GSS_C_NO_NAME;
503 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
504 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
505 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
511 gss_OID mech_type = GSS_C_NO_OID;
512 gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
513 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
514 struct mag_conn *mc = NULL;
518 type = ap_auth_type(req);
519 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
523 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
525 if (!cfg->allowed_mechs) {
526 /* Try to fetch the default set if not explicitly configured */
527 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
528 (void)mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
529 &server_cred, &cfg->allowed_mechs);
530 (void)gss_release_cred(&min, &server_cred);
533 /* implicit auth for subrequests if main auth already happened */
534 if (!ap_is_initial_req(req) && req->main != NULL) {
535 type = ap_auth_type(req->main);
536 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
537 /* warn if the subrequest location and the main request
538 * location have different configs */
539 if (cfg != ap_get_module_config(req->main->per_dir_config,
540 &auth_gssapi_module)) {
541 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
542 req, "Subrequest authentication bypass on "
543 "location with different configuration!");
545 if (req->main->user) {
546 req->user = apr_pstrdup(req->pool, req->main->user);
549 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
550 "The main request is tasked to establish the "
551 "security context, can't proceed!");
552 return HTTP_UNAUTHORIZED;
555 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
556 "Subrequest GSSAPI auth with no auth on the main "
557 "request. This operation may fail if other "
558 "subrequests already established a context or the "
559 "mechanism requires multiple roundtrips.");
564 if (!mag_conn_is_https(req->connection)) {
565 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
566 "Not a TLS connection, refusing to authenticate!");
571 if (cfg->gss_conn_ctx) {
572 mc = (struct mag_conn *)ap_get_module_config(
573 req->connection->conn_config,
574 &auth_gssapi_module);
576 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
577 "Failed to retrieve connection context!");
582 /* if available, session always supersedes connection bound data */
583 if (cfg->use_sessions) {
584 mag_check_session(req, cfg, &mc);
587 auth_header = apr_table_get(req->headers_in, "Authorization");
590 if (mc->established &&
591 (auth_header == NULL) &&
592 (mc->auth_type != AUTH_TYPE_BASIC)) {
593 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
594 "Already established context found!");
595 mag_set_req_data(req, cfg, mc);
604 /* We can proceed only if we do have an auth header */
605 if (!auth_header) goto done;
607 auth_header_type = ap_getword_white(req->pool, &auth_header);
608 if (!auth_header_type) goto done;
610 for (i = 0; auth_types[i] != NULL; i++) {
611 if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
618 case AUTH_TYPE_NEGOTIATE:
619 if (!parse_auth_header(req->pool, &auth_header, &input)) {
623 case AUTH_TYPE_BASIC:
624 if (!cfg->use_basic_auth) {
628 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
629 if (!ba_pwd.value) goto done;
630 ba_user.value = ap_getword_nulls_nc(req->pool,
631 (char **)&ba_pwd.value, ':');
632 if (!ba_user.value) goto done;
633 if (((char *)ba_user.value)[0] == '\0' ||
634 ((char *)ba_pwd.value)[0] == '\0') {
635 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
636 "Invalid empty user or password for Basic Auth");
639 ba_user.length = strlen(ba_user.value);
640 ba_pwd.length = strlen(ba_pwd.value);
642 if (mc && mc->established &&
643 mag_basic_check(cfg, mc, ba_user, ba_pwd)) {
644 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
645 "Already established BASIC AUTH context found!");
646 mag_set_req_data(req, cfg, mc);
653 case AUTH_TYPE_RAW_NTLM:
654 if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
655 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
656 "NTLM Authentication is not allowed!");
660 if (!parse_auth_header(req->pool, &auth_header, &input)) {
664 desired_mechs = discard_const(&gss_mech_set_ntlmssp);
671 if (mc && mc->established) {
672 /* if we are re-authenticating make sure the conn context
673 * is cleaned up so we do not accidentally reuse an existing
674 * established context */
678 req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
680 #ifdef HAVE_CRED_STORE
681 if (cfg->use_s4u2proxy) {
682 cred_usage = GSS_C_BOTH;
685 if (!mag_acquire_creds(req, cfg, desired_mechs,
686 cred_usage, &acquired_cred, NULL)) {
690 if (auth_type == AUTH_TYPE_BASIC) {
691 if (mag_auth_basic(req, cfg, ba_user, ba_pwd,
692 acquired_cred, cred_usage,
694 &delegated_cred, &vtime)) {
700 if (auth_type == AUTH_TYPE_NEGOTIATE &&
701 cfg->allowed_mechs != GSS_C_NO_OID_SET) {
702 maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
703 if (GSS_ERROR(maj)) {
704 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
705 mag_error(req, "gss_set_neg_mechs() failed",
711 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
712 &input, GSS_C_NO_CHANNEL_BINDINGS,
713 &client, &mech_type, &output, NULL, &vtime,
715 if (GSS_ERROR(maj)) {
716 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
717 mag_error(req, "gss_accept_sec_context() failed",
720 } else if (maj == GSS_S_CONTINUE_NEEDED) {
722 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
723 "Mechanism needs continuation but neither "
724 "GssapiConnectionBound nor "
725 "GssapiUseSessions are available");
726 gss_release_buffer(&min, &output);
729 /* auth not complete send token and wait next packet */
734 /* Always set the GSS name in an env var */
735 maj = gss_display_name(&min, client, &name, NULL);
736 if (GSS_ERROR(maj)) {
737 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
738 mag_error(req, "gss_display_name() failed",
742 clientname = apr_pstrndup(req->pool, name.value, name.length);
743 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
744 expiration = time(NULL) + vtime;
745 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
746 apr_psprintf(req->pool, "%ld", (long)expiration));
748 #ifdef HAVE_CRED_STORE
749 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
750 char *ccachefile = NULL;
752 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
753 delegated_cred, &ccachefile);
756 mag_set_KRB5CCANME(req, ccachefile);
760 mc->delegated = true;
765 if (cfg->map_to_local) {
766 maj = gss_localname(&min, client, mech_type, &lname);
767 if (maj != GSS_S_COMPLETE) {
768 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
769 mag_error(req, "gss_localname() failed", maj, min));
772 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
774 req->user = clientname;
778 mc->user_name = apr_pstrdup(mc->pool, req->user);
779 mc->gss_name = apr_pstrdup(mc->pool, clientname);
780 mc->established = true;
781 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
782 vtime = MIN_SESS_EXP_TIME;
784 mc->expiration = expiration;
785 mc->auth_type = auth_type;
786 if (auth_type == AUTH_TYPE_BASIC) {
787 mag_basic_cache(cfg, mc, ba_user, ba_pwd);
789 if (cfg->use_sessions) {
790 mag_attempt_session(req, cfg, mc);
794 if (cfg->send_persist)
795 apr_table_set(req->headers_out, "Persistent-Auth",
796 cfg->gss_conn_ctx ? "true" : "false");
801 if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
802 int prefixlen = strlen(auth_types[auth_type]) + 1;
803 replen = apr_base64_encode_len(output.length) + 1;
804 reply = apr_pcalloc(req->pool, prefixlen + replen);
806 memcpy(reply, auth_types[auth_type], prefixlen - 1);
807 reply[prefixlen - 1] = ' ';
808 apr_base64_encode(&reply[prefixlen], output.value, output.length);
809 apr_table_add(req->err_headers_out,
810 "WWW-Authenticate", reply);
812 } else if (ret == HTTP_UNAUTHORIZED) {
813 apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
814 if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
815 apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
817 if (cfg->use_basic_auth) {
818 apr_table_add(req->err_headers_out,
820 apr_psprintf(req->pool, "Basic realm=\"%s\"",
824 if (ctx != GSS_C_NO_CONTEXT)
825 gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
826 gss_release_cred(&min, &acquired_cred);
827 gss_release_cred(&min, &delegated_cred);
828 gss_release_buffer(&min, &output);
829 gss_release_name(&min, &client);
830 gss_release_buffer(&min, &name);
831 gss_release_buffer(&min, &lname);
836 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
838 struct mag_config *cfg;
840 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
846 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
848 struct mag_config *cfg = (struct mag_config *)mconfig;
849 cfg->ssl_only = on ? true : false;
853 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
855 struct mag_config *cfg = (struct mag_config *)mconfig;
856 cfg->map_to_local = on ? true : false;
860 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
862 struct mag_config *cfg = (struct mag_config *)mconfig;
863 cfg->gss_conn_ctx = on ? true : false;
867 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
869 struct mag_config *cfg = (struct mag_config *)mconfig;
870 cfg->send_persist = on ? true : false;
874 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
876 struct mag_config *cfg = (struct mag_config *)mconfig;
877 cfg->use_sessions = on ? true : false;
881 #ifdef HAVE_CRED_STORE
882 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
884 struct mag_config *cfg = (struct mag_config *)mconfig;
885 cfg->use_s4u2proxy = on ? true : false;
887 if (cfg->deleg_ccache_dir == NULL) {
888 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
894 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
896 struct mag_config *cfg = (struct mag_config *)mconfig;
903 if (strncmp(w, "key:", 4) != 0) {
904 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
905 "Invalid key format, expected prefix 'key:'");
910 l = apr_base64_decode_len(k);
911 val = apr_palloc(parms->temp_pool, l);
913 keys.length = (int)apr_base64_decode_binary(val, k);
914 keys.value = (unsigned char *)val;
916 if (keys.length != 32) {
917 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
918 "Invalid key length, expected 32 got %d", keys.length);
922 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
924 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
925 "Failed to import sealing key!");
930 #ifdef HAVE_CRED_STORE
932 #define MAX_CRED_OPTIONS 10
934 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
937 struct mag_config *cfg = (struct mag_config *)mconfig;
938 gss_key_value_element_desc *elements;
947 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
948 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
952 key = apr_pstrndup(parms->pool, w, (p-w));
953 value = apr_pstrdup(parms->pool, p + 1);
955 if (!cfg->cred_store) {
956 cfg->cred_store = apr_pcalloc(parms->pool,
957 sizeof(gss_key_value_set_desc));
958 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
959 cfg->cred_store->elements = apr_palloc(parms->pool, size);
962 elements = cfg->cred_store->elements;
963 count = cfg->cred_store->count;
965 if (count >= MAX_CRED_OPTIONS) {
966 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
967 "Too many GssapiCredStore options (MAX: %d)",
971 cfg->cred_store->count++;
973 elements[count].key = key;
974 elements[count].value = value;
979 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
982 struct mag_config *cfg = (struct mag_config *)mconfig;
984 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
990 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
992 struct mag_config *cfg = (struct mag_config *)mconfig;
994 cfg->use_basic_auth = on ? true : false;
998 #define MAX_ALLOWED_MECHS 10
1000 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
1003 struct mag_config *cfg = (struct mag_config *)mconfig;
1007 if (!cfg->allowed_mechs) {
1008 cfg->allowed_mechs = apr_pcalloc(parms->pool,
1009 sizeof(gss_OID_set_desc));
1010 size = sizeof(gss_OID) * MAX_ALLOWED_MECHS;
1011 cfg->allowed_mechs->elements = apr_palloc(parms->pool, size);
1014 if (strcmp(w, "krb5") == 0) {
1015 oid = gss_mech_krb5;
1016 } else if (strcmp(w, "iakerb") == 0) {
1017 oid = gss_mech_iakerb;
1018 } else if (strcmp(w, "ntlmssp") == 0) {
1019 oid = &gss_mech_ntlmssp;
1021 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1022 "Unrecognized GSSAPI Mechanism: %s", w);
1026 if (cfg->allowed_mechs->count >= MAX_ALLOWED_MECHS) {
1027 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
1028 "Too many GssapiAllowedMech options (MAX: %d)",
1032 cfg->allowed_mechs->elements[cfg->allowed_mechs->count] = *oid;
1033 cfg->allowed_mechs->count++;
1038 static const command_rec mag_commands[] = {
1039 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
1040 "Work only if connection is SSL Secured"),
1041 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
1042 "Translate principals to local names"),
1043 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
1044 "Authentication is bound to the TCP connection"),
1045 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
1046 "Send Persitent-Auth header according to connection bound"),
1047 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
1048 "Authentication uses mod_sessions to hold status"),
1049 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
1050 "Key Used to seal session data."),
1051 #ifdef HAVE_CRED_STORE
1052 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
1053 "Initializes credentials for s4u2proxy usage"),
1054 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
1055 "Credential Store"),
1056 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
1057 OR_AUTHCFG, "Directory to store delegated credentials"),
1059 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1060 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
1061 "Allows use of Basic Auth for authentication"),
1063 AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
1064 "Allowed Mechanisms"),
1069 mag_register_hooks(apr_pool_t *p)
1071 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1072 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1073 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1076 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1078 STANDARD20_MODULE_STUFF,
1079 mag_create_dir_config,