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);
114 memset(mc, 0, sizeof(struct mag_conn));
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 void mag_set_req_data(request_rec *req,
292 struct mag_config *cfg,
295 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
296 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
297 apr_psprintf(req->pool,
298 "%ld", (long)mc->expiration));
299 req->ap_auth_type = apr_pstrdup(req->pool,
300 auth_types[mc->auth_type]);
301 req->user = apr_pstrdup(req->pool, mc->user_name);
302 if (cfg->deleg_ccache_dir && mc->delegated) {
304 ccname = mag_gss_name_to_ccache_name(req,
305 cfg->deleg_ccache_dir,
308 mag_set_KRB5CCANME(req, ccname);
313 static int mag_auth(request_rec *req)
317 struct mag_config *cfg;
318 const char *auth_header;
319 char *auth_header_type;
320 int ret = HTTP_UNAUTHORIZED;
321 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
323 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
324 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
325 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
326 gss_buffer_desc ba_user;
327 gss_buffer_desc ba_pwd;
328 gss_name_t client = GSS_C_NO_NAME;
329 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
330 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
331 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
332 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
333 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
340 gss_OID mech_type = GSS_C_NO_OID;
341 gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
342 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
343 struct mag_conn *mc = NULL;
344 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
345 gss_name_t server = GSS_C_NO_NAME;
346 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
347 const char *user_ccache = NULL;
348 const char *orig_ccache = NULL;
350 uint32_t init_flags = 0;
354 type = ap_auth_type(req);
355 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
359 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
361 if (!cfg->allowed_mechs) {
362 /* Try to fetch the default set if not explicitly configured */
363 (void)mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
364 &server_cred, &cfg->allowed_mechs);
365 (void)gss_release_cred(&min, &server_cred);
368 /* implicit auth for subrequests if main auth already happened */
369 if (!ap_is_initial_req(req) && req->main != NULL) {
370 type = ap_auth_type(req->main);
371 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
372 /* warn if the subrequest location and the main request
373 * location have different configs */
374 if (cfg != ap_get_module_config(req->main->per_dir_config,
375 &auth_gssapi_module)) {
376 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
377 req, "Subrequest authentication bypass on "
378 "location with different configuration!");
380 if (req->main->user) {
381 req->user = apr_pstrdup(req->pool, req->main->user);
384 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
385 "The main request is tasked to establish the "
386 "security context, can't proceed!");
387 return HTTP_UNAUTHORIZED;
390 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
391 "Subrequest GSSAPI auth with no auth on the main "
392 "request. This operation may fail if other "
393 "subrequests already established a context or the "
394 "mechanism requires multiple roundtrips.");
399 if (!mag_conn_is_https(req->connection)) {
400 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
401 "Not a TLS connection, refusing to authenticate!");
406 if (cfg->gss_conn_ctx) {
407 mc = (struct mag_conn *)ap_get_module_config(
408 req->connection->conn_config,
409 &auth_gssapi_module);
411 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
412 "Failed to retrieve connection context!");
417 /* if available, session always supersedes connection bound data */
418 if (cfg->use_sessions) {
419 mag_check_session(req, cfg, &mc);
422 auth_header = apr_table_get(req->headers_in, "Authorization");
425 /* register the context in the memory pool, so it can be freed
426 * when the connection/request is terminated */
427 apr_pool_cleanup_register(mc->parent, (void *) mc,
428 mag_conn_destroy, apr_pool_cleanup_null);
430 if (mc->established && !auth_header) {
431 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
432 "Already established context found!");
433 mag_set_req_data(req, cfg, mc);
442 /* We can proceed only if we do have an auth header */
443 if (!auth_header) goto done;
445 auth_header_type = ap_getword_white(req->pool, &auth_header);
446 if (!auth_header_type) goto done;
448 for (i = 0; auth_types[i] != NULL; i++) {
449 if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
455 if (mc && mc->established && auth_type != AUTH_TYPE_BASIC) {
456 /* if we are re-authenticating make sure the conn context
457 * is cleaned up so we do not accidentally reuse an existing
458 * established context */
459 mag_conn_destroy(mc);
463 case AUTH_TYPE_NEGOTIATE:
464 if (!parse_auth_header(req->pool, &auth_header, &input)) {
468 case AUTH_TYPE_BASIC:
469 if (!cfg->use_basic_auth) {
473 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
474 if (!ba_pwd.value) goto done;
475 ba_user.value = ap_getword_nulls_nc(req->pool,
476 (char **)&ba_pwd.value, ':');
477 if (!ba_user.value) goto done;
478 if (((char *)ba_user.value)[0] == '\0' ||
479 ((char *)ba_pwd.value)[0] == '\0') {
480 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
481 "Invalid empty user or password for Basic Auth");
484 ba_user.length = strlen(ba_user.value);
485 ba_pwd.length = strlen(ba_pwd.value);
487 if (mc && mc->established) {
488 if (mag_basic_check(cfg, mc, ba_user, ba_pwd)) {
489 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
490 "Already established BASIC AUTH context found!");
491 mag_set_req_data(req, cfg, mc);
495 mag_conn_destroy(mc);
499 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
500 if (GSS_ERROR(maj)) {
501 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
503 mag_error(req, "gss_import_name() failed",
507 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
508 /* Set a per-thread ccache in case we are using kerberos,
509 * it is not elegant but avoids interference between threads */
510 long long unsigned int rndname;
512 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
513 sizeof(long long unsigned int));
514 if (rs != APR_SUCCESS) {
515 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
516 "Failed to generate random ccache name");
519 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
520 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
521 if (GSS_ERROR(maj)) {
522 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
524 mag_error(req, "gss_krb5_ccache_name() "
525 "failed", maj, min));
529 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
533 &user_cred, NULL, NULL);
534 if (GSS_ERROR(maj)) {
535 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
537 mag_error(req, "gss_acquire_cred_with_password() "
538 "failed", maj, min));
541 gss_release_name(&min, &client);
544 case AUTH_TYPE_RAW_NTLM:
545 if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
546 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
547 "NTLM Authentication is not allowed!");
551 if (!parse_auth_header(req->pool, &auth_header, &input)) {
555 desired_mechs = discard_const(&gss_mech_set_ntlmssp);
562 req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
564 #ifdef HAVE_CRED_STORE
565 if (cfg->use_s4u2proxy) {
566 cred_usage = GSS_C_BOTH;
569 if (!mag_acquire_creds(req, cfg, desired_mechs,
570 cred_usage, &acquired_cred, NULL)) {
574 if (auth_type == AUTH_TYPE_BASIC) {
575 if (cred_usage == GSS_C_BOTH) {
576 /* If GSS_C_BOTH is used then inquire_cred will return the client
577 * name instead of the SPN of the server credentials. Therefore we
578 * need to acquire a different set of credential setting
579 * GSS_C_ACCEPT explicitly */
580 if (!mag_acquire_creds(req, cfg, cfg->allowed_mechs,
581 GSS_C_ACCEPT, &server_cred, NULL)) {
585 server_cred = acquired_cred;
587 maj = gss_inquire_cred(&min, server_cred, &server,
589 if (GSS_ERROR(maj)) {
590 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
591 "%s", mag_error(req, "gss_inquired_cred_() "
592 "failed", maj, min));
595 if (server_cred != acquired_cred) {
596 gss_release_cred(&min, &server_cred);
599 #ifdef HAVE_CRED_STORE
600 if (cfg->deleg_ccache_dir) {
601 /* delegate ourselves credentials so we store them as requested */
602 init_flags |= GSS_C_DELEG_FLAG;
606 /* output and input are inverted here, this is intentional */
607 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
608 GSS_C_NO_OID, init_flags, 300,
609 GSS_C_NO_CHANNEL_BINDINGS, &output,
610 NULL, &input, NULL, NULL);
611 if (GSS_ERROR(maj)) {
612 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
613 "%s", mag_error(req, "gss_init_sec_context() "
614 "failed", maj, min));
619 if (auth_type == AUTH_TYPE_NEGOTIATE &&
620 cfg->allowed_mechs != GSS_C_NO_OID_SET) {
621 maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
622 if (GSS_ERROR(maj)) {
623 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
624 mag_error(req, "gss_set_neg_mechs() failed",
630 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
631 &input, GSS_C_NO_CHANNEL_BINDINGS,
632 &client, &mech_type, &output, &flags, &vtime,
634 if (GSS_ERROR(maj)) {
635 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
636 mag_error(req, "gss_accept_sec_context() failed",
640 if (auth_type == AUTH_TYPE_BASIC) {
641 while (maj == GSS_S_CONTINUE_NEEDED) {
642 gss_release_buffer(&min, &input);
643 /* output and input are inverted here, this is intentional */
644 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
645 GSS_C_NO_OID, init_flags, 300,
646 GSS_C_NO_CHANNEL_BINDINGS, &output,
647 NULL, &input, NULL, NULL);
648 if (GSS_ERROR(maj)) {
649 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
650 "%s", mag_error(req, "gss_init_sec_context() "
651 "failed", maj, min));
654 gss_release_buffer(&min, &output);
655 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
656 &input, GSS_C_NO_CHANNEL_BINDINGS,
657 &client, &mech_type, &output, &flags,
658 &vtime, &delegated_cred);
659 if (GSS_ERROR(maj)) {
660 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
661 "%s", mag_error(req, "gss_accept_sec_context()"
662 " failed", maj, min));
666 } else if (maj == GSS_S_CONTINUE_NEEDED) {
668 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
669 "Mechanism needs continuation but neither "
670 "GssapiConnectionBound nor "
671 "GssapiUseSessions are available");
672 gss_release_buffer(&min, &output);
675 /* auth not complete send token and wait next packet */
679 /* Always set the GSS name in an env var */
680 maj = gss_display_name(&min, client, &name, NULL);
681 if (GSS_ERROR(maj)) {
682 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
683 mag_error(req, "gss_display_name() failed",
687 clientname = apr_pstrndup(req->pool, name.value, name.length);
688 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
689 expiration = time(NULL) + vtime;
690 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
691 apr_psprintf(req->pool, "%ld", (long)expiration));
693 #ifdef HAVE_CRED_STORE
694 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
695 char *ccachefile = NULL;
697 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
698 delegated_cred, &ccachefile);
701 mag_set_KRB5CCANME(req, ccachefile);
705 mc->delegated = true;
710 if (cfg->map_to_local) {
711 maj = gss_localname(&min, client, mech_type, &lname);
712 if (maj != GSS_S_COMPLETE) {
713 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
714 mag_error(req, "gss_localname() failed", maj, min));
717 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
719 req->user = clientname;
723 mc->user_name = apr_pstrdup(mc->parent, req->user);
724 mc->gss_name = apr_pstrdup(mc->parent, clientname);
725 mc->established = true;
726 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
727 vtime = MIN_SESS_EXP_TIME;
729 mc->expiration = expiration;
730 mc->auth_type = auth_type;
731 if (auth_type == AUTH_TYPE_BASIC) {
732 mag_basic_cache(cfg, mc, ba_user, ba_pwd);
734 if (cfg->use_sessions) {
735 mag_attempt_session(req, cfg, mc);
739 if (cfg->send_persist)
740 apr_table_set(req->headers_out, "Persistent-Auth",
741 cfg->gss_conn_ctx ? "true" : "false");
746 if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
747 int prefixlen = strlen(auth_types[auth_type]) + 1;
748 replen = apr_base64_encode_len(output.length) + 1;
749 reply = apr_pcalloc(req->pool, prefixlen + replen);
751 memcpy(reply, auth_types[auth_type], prefixlen - 1);
752 reply[prefixlen - 1] = ' ';
753 apr_base64_encode(&reply[prefixlen], output.value, output.length);
754 apr_table_add(req->err_headers_out,
755 "WWW-Authenticate", reply);
757 } else if (ret == HTTP_UNAUTHORIZED) {
758 apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
759 if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
760 apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
762 if (cfg->use_basic_auth) {
763 apr_table_add(req->err_headers_out,
765 apr_psprintf(req->pool, "Basic realm=\"%s\"",
769 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
770 if (user_ccache != NULL) {
771 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
772 if (maj != GSS_S_COMPLETE) {
773 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
774 "Failed to restore per-thread ccache, %s",
775 mag_error(req, "gss_krb5_ccache_name() "
776 "failed", maj, min));
780 if (ctx != GSS_C_NO_CONTEXT)
781 gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
782 gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER);
783 gss_release_cred(&min, &user_cred);
784 gss_release_cred(&min, &acquired_cred);
785 gss_release_cred(&min, &delegated_cred);
786 gss_release_buffer(&min, &output);
787 gss_release_name(&min, &client);
788 gss_release_name(&min, &server);
789 gss_release_buffer(&min, &name);
790 gss_release_buffer(&min, &lname);
795 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
797 struct mag_config *cfg;
799 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
805 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
807 struct mag_config *cfg = (struct mag_config *)mconfig;
808 cfg->ssl_only = on ? true : false;
812 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
814 struct mag_config *cfg = (struct mag_config *)mconfig;
815 cfg->map_to_local = on ? true : false;
819 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
821 struct mag_config *cfg = (struct mag_config *)mconfig;
822 cfg->gss_conn_ctx = on ? true : false;
826 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
828 struct mag_config *cfg = (struct mag_config *)mconfig;
829 cfg->send_persist = on ? true : false;
833 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
835 struct mag_config *cfg = (struct mag_config *)mconfig;
836 cfg->use_sessions = on ? true : false;
840 #ifdef HAVE_CRED_STORE
841 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
843 struct mag_config *cfg = (struct mag_config *)mconfig;
844 cfg->use_s4u2proxy = on ? true : false;
846 if (cfg->deleg_ccache_dir == NULL) {
847 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
853 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
855 struct mag_config *cfg = (struct mag_config *)mconfig;
862 if (strncmp(w, "key:", 4) != 0) {
863 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
864 "Invalid key format, expected prefix 'key:'");
869 l = apr_base64_decode_len(k);
870 val = apr_palloc(parms->temp_pool, l);
872 keys.length = (int)apr_base64_decode_binary(val, k);
873 keys.value = (unsigned char *)val;
875 if (keys.length != 32) {
876 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
877 "Invalid key length, expected 32 got %d", keys.length);
881 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
883 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
884 "Failed to import sealing key!");
889 #ifdef HAVE_CRED_STORE
891 #define MAX_CRED_OPTIONS 10
893 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
896 struct mag_config *cfg = (struct mag_config *)mconfig;
897 gss_key_value_element_desc *elements;
906 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
907 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
911 key = apr_pstrndup(parms->pool, w, (p-w));
912 value = apr_pstrdup(parms->pool, p + 1);
914 if (!cfg->cred_store) {
915 cfg->cred_store = apr_pcalloc(parms->pool,
916 sizeof(gss_key_value_set_desc));
917 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
918 cfg->cred_store->elements = apr_palloc(parms->pool, size);
921 elements = cfg->cred_store->elements;
922 count = cfg->cred_store->count;
924 if (count >= MAX_CRED_OPTIONS) {
925 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
926 "Too many GssapiCredStore options (MAX: %d)",
930 cfg->cred_store->count++;
932 elements[count].key = key;
933 elements[count].value = value;
938 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
941 struct mag_config *cfg = (struct mag_config *)mconfig;
943 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
949 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
951 struct mag_config *cfg = (struct mag_config *)mconfig;
953 cfg->use_basic_auth = on ? true : false;
957 #define MAX_ALLOWED_MECHS 10
959 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
962 struct mag_config *cfg = (struct mag_config *)mconfig;
966 if (!cfg->allowed_mechs) {
967 cfg->allowed_mechs = apr_pcalloc(parms->pool,
968 sizeof(gss_OID_set_desc));
969 size = sizeof(gss_OID) * MAX_ALLOWED_MECHS;
970 cfg->allowed_mechs->elements = apr_palloc(parms->pool, size);
973 if (strcmp(w, "krb5") == 0) {
975 } else if (strcmp(w, "iakerb") == 0) {
976 oid = gss_mech_iakerb;
977 } else if (strcmp(w, "ntlmssp") == 0) {
978 oid = &gss_mech_ntlmssp;
980 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
981 "Unrecognized GSSAPI Mechanism: %s", w);
985 if (cfg->allowed_mechs->count >= MAX_ALLOWED_MECHS) {
986 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
987 "Too many GssapiAllowedMech options (MAX: %d)",
991 cfg->allowed_mechs->elements[cfg->allowed_mechs->count] = *oid;
992 cfg->allowed_mechs->count++;
997 static const command_rec mag_commands[] = {
998 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
999 "Work only if connection is SSL Secured"),
1000 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
1001 "Translate principals to local names"),
1002 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
1003 "Authentication is bound to the TCP connection"),
1004 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
1005 "Send Persitent-Auth header according to connection bound"),
1006 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
1007 "Authentication uses mod_sessions to hold status"),
1008 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
1009 "Key Used to seal session data."),
1010 #ifdef HAVE_CRED_STORE
1011 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
1012 "Initializes credentials for s4u2proxy usage"),
1013 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
1014 "Credential Store"),
1015 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
1016 OR_AUTHCFG, "Directory to store delegated credentials"),
1018 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
1019 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
1020 "Allows use of Basic Auth for authentication"),
1022 AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
1023 "Allowed Mechanisms"),
1028 mag_register_hooks(apr_pool_t *p)
1030 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1031 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1032 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1035 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1037 STANDARD20_MODULE_STUFF,
1038 mag_create_dir_config,