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);
433 auth_header = apr_table_get(req->headers_in, "Authorization");
434 if (!auth_header) goto done;
436 auth_header_type = ap_getword_white(req->pool, &auth_header);
437 if (!auth_header_type) goto done;
439 for (i = 0; auth_types[i] != NULL; i++) {
440 if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
447 case AUTH_TYPE_NEGOTIATE:
448 if (!parse_auth_header(req->pool, &auth_header, &input)) {
452 case AUTH_TYPE_BASIC:
453 if (!cfg->use_basic_auth) {
457 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
458 if (!ba_pwd.value) goto done;
459 ba_user.value = ap_getword_nulls_nc(req->pool,
460 (char **)&ba_pwd.value, ':');
461 if (!ba_user.value) goto done;
462 if (((char *)ba_user.value)[0] == '\0' ||
463 ((char *)ba_pwd.value)[0] == '\0') {
464 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
465 "Invalid empty user or password for Basic Auth");
468 ba_user.length = strlen(ba_user.value);
469 ba_pwd.length = strlen(ba_pwd.value);
470 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
471 if (GSS_ERROR(maj)) {
472 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
474 mag_error(req, "gss_import_name() failed",
478 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
479 /* Set a per-thread ccache in case we are using kerberos,
480 * it is not elegant but avoids interference between threads */
481 long long unsigned int rndname;
483 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
484 sizeof(long long unsigned int));
485 if (rs != APR_SUCCESS) {
486 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
487 "Failed to generate random ccache name");
490 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
491 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
492 if (GSS_ERROR(maj)) {
493 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
495 mag_error(req, "gss_krb5_ccache_name() "
496 "failed", maj, min));
500 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
504 &user_cred, NULL, NULL);
505 if (GSS_ERROR(maj)) {
506 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
508 mag_error(req, "gss_acquire_cred_with_password() "
509 "failed", maj, min));
512 gss_release_name(&min, &client);
515 case AUTH_TYPE_RAW_NTLM:
516 if (!is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
517 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
518 "NTLM Authentication is not allowed!");
522 if (!parse_auth_header(req->pool, &auth_header, &input)) {
526 desired_mechs = discard_const(&gss_mech_set_ntlmssp);
533 req->ap_auth_type = apr_pstrdup(req->pool, auth_types[auth_type]);
535 #ifdef HAVE_CRED_STORE
536 if (cfg->use_s4u2proxy) {
537 cred_usage = GSS_C_BOTH;
540 if (!mag_acquire_creds(req, cfg, desired_mechs,
541 cred_usage, &acquired_cred, NULL)) {
545 if (auth_type == AUTH_TYPE_BASIC) {
546 if (cred_usage == GSS_C_BOTH) {
547 /* If GSS_C_BOTH is used then inquire_cred will return the client
548 * name instead of the SPN of the server credentials. Therefore we
549 * need to acquire a different set of credential setting
550 * GSS_C_ACCEPT explicitly */
551 if (!mag_acquire_creds(req, cfg, cfg->allowed_mechs,
552 GSS_C_ACCEPT, &server_cred, NULL)) {
556 server_cred = acquired_cred;
558 maj = gss_inquire_cred(&min, server_cred, &server,
560 if (GSS_ERROR(maj)) {
561 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
562 "%s", mag_error(req, "gss_inquired_cred_() "
563 "failed", maj, min));
566 if (server_cred != acquired_cred) {
567 gss_release_cred(&min, &server_cred);
570 #ifdef HAVE_CRED_STORE
571 if (cfg->deleg_ccache_dir) {
572 /* delegate ourselves credentials so we store them as requested */
573 init_flags |= GSS_C_DELEG_FLAG;
577 /* output and input are inverted here, this is intentional */
578 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
579 GSS_C_NO_OID, init_flags, 300,
580 GSS_C_NO_CHANNEL_BINDINGS, &output,
581 NULL, &input, NULL, NULL);
582 if (GSS_ERROR(maj)) {
583 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
584 "%s", mag_error(req, "gss_init_sec_context() "
585 "failed", maj, min));
590 if (auth_type == AUTH_TYPE_NEGOTIATE &&
591 cfg->allowed_mechs != GSS_C_NO_OID_SET) {
592 maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
593 if (GSS_ERROR(maj)) {
594 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
595 mag_error(req, "gss_set_neg_mechs() failed",
601 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
602 &input, GSS_C_NO_CHANNEL_BINDINGS,
603 &client, &mech_type, &output, &flags, &vtime,
605 if (GSS_ERROR(maj)) {
606 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
607 mag_error(req, "gss_accept_sec_context() failed",
611 if (auth_type == AUTH_TYPE_BASIC) {
613 apr_pool_cleanup_run(mc->parent, mc, mag_conn_destroy);
616 while (maj == GSS_S_CONTINUE_NEEDED) {
617 gss_release_buffer(&min, &input);
618 /* output and input are inverted here, this is intentional */
619 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
620 GSS_C_NO_OID, init_flags, 300,
621 GSS_C_NO_CHANNEL_BINDINGS, &output,
622 NULL, &input, NULL, NULL);
623 if (GSS_ERROR(maj)) {
624 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
625 "%s", mag_error(req, "gss_init_sec_context() "
626 "failed", maj, min));
629 gss_release_buffer(&min, &output);
630 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
631 &input, GSS_C_NO_CHANNEL_BINDINGS,
632 &client, &mech_type, &output, &flags,
633 &vtime, &delegated_cred);
634 if (GSS_ERROR(maj)) {
635 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
636 "%s", mag_error(req, "gss_accept_sec_context()"
637 " failed", maj, min));
641 } else if (maj == GSS_S_CONTINUE_NEEDED) {
643 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
644 "Mechanism needs continuation but neither "
645 "GssapiConnectionBound nor "
646 "GssapiUseSessions are available");
647 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
648 gss_release_buffer(&min, &output);
651 /* auth not complete send token and wait next packet */
655 /* Always set the GSS name in an env var */
656 maj = gss_display_name(&min, client, &name, NULL);
657 if (GSS_ERROR(maj)) {
658 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
659 mag_error(req, "gss_display_name() failed",
663 clientname = apr_pstrndup(req->pool, name.value, name.length);
664 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
665 expiration = time(NULL) + vtime;
666 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
667 apr_psprintf(req->pool, "%ld", (long)expiration));
669 #ifdef HAVE_CRED_STORE
670 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
671 char *ccachefile = NULL;
673 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
674 delegated_cred, &ccachefile);
677 mag_set_KRB5CCANME(req, ccachefile);
681 mc->delegated = true;
686 if (cfg->map_to_local) {
687 maj = gss_localname(&min, client, mech_type, &lname);
688 if (maj != GSS_S_COMPLETE) {
689 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
690 mag_error(req, "gss_localname() failed", maj, min));
693 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
695 req->user = clientname;
699 mc->user_name = apr_pstrdup(mc->parent, req->user);
700 mc->gss_name = apr_pstrdup(mc->parent, clientname);
701 mc->established = true;
702 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
703 vtime = MIN_SESS_EXP_TIME;
705 mc->expiration = expiration;
706 if (cfg->use_sessions) {
707 mag_attempt_session(req, cfg, mc);
709 mc->auth_type = auth_type;
712 if (cfg->send_persist)
713 apr_table_set(req->headers_out, "Persistent-Auth",
714 cfg->gss_conn_ctx ? "true" : "false");
719 if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
720 int prefixlen = strlen(auth_types[auth_type]) + 1;
721 replen = apr_base64_encode_len(output.length) + 1;
722 reply = apr_pcalloc(req->pool, prefixlen + replen);
724 memcpy(reply, auth_types[auth_type], prefixlen - 1);
725 reply[prefixlen - 1] = ' ';
726 apr_base64_encode(&reply[prefixlen], output.value, output.length);
727 apr_table_add(req->err_headers_out,
728 "WWW-Authenticate", reply);
730 } else if (ret == HTTP_UNAUTHORIZED) {
731 apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
732 if (is_mech_allowed(cfg, &gss_mech_ntlmssp)) {
733 apr_table_add(req->err_headers_out, "WWW-Authenticate", "NTLM");
735 if (cfg->use_basic_auth) {
736 apr_table_add(req->err_headers_out,
738 apr_psprintf(req->pool, "Basic realm=\"%s\"",
742 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
743 if (user_ccache != NULL) {
744 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
745 if (maj != GSS_S_COMPLETE) {
746 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
747 "Failed to restore per-thread ccache, %s",
748 mag_error(req, "gss_krb5_ccache_name() "
749 "failed", maj, min));
753 gss_delete_sec_context(&min, &user_ctx, &output);
754 gss_release_cred(&min, &user_cred);
755 gss_release_cred(&min, &acquired_cred);
756 gss_release_cred(&min, &delegated_cred);
757 gss_release_buffer(&min, &output);
758 gss_release_name(&min, &client);
759 gss_release_name(&min, &server);
760 gss_release_buffer(&min, &name);
761 gss_release_buffer(&min, &lname);
766 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
768 struct mag_config *cfg;
770 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
776 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
778 struct mag_config *cfg = (struct mag_config *)mconfig;
779 cfg->ssl_only = on ? true : false;
783 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
785 struct mag_config *cfg = (struct mag_config *)mconfig;
786 cfg->map_to_local = on ? true : false;
790 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
792 struct mag_config *cfg = (struct mag_config *)mconfig;
793 cfg->gss_conn_ctx = on ? true : false;
797 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
799 struct mag_config *cfg = (struct mag_config *)mconfig;
800 cfg->send_persist = on ? true : false;
804 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
806 struct mag_config *cfg = (struct mag_config *)mconfig;
807 cfg->use_sessions = on ? true : false;
811 #ifdef HAVE_CRED_STORE
812 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
814 struct mag_config *cfg = (struct mag_config *)mconfig;
815 cfg->use_s4u2proxy = on ? true : false;
817 if (cfg->deleg_ccache_dir == NULL) {
818 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
824 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
826 struct mag_config *cfg = (struct mag_config *)mconfig;
833 if (strncmp(w, "key:", 4) != 0) {
834 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
835 "Invalid key format, expected prefix 'key:'");
840 l = apr_base64_decode_len(k);
841 val = apr_palloc(parms->temp_pool, l);
843 keys.length = (int)apr_base64_decode_binary(val, k);
844 keys.value = (unsigned char *)val;
846 if (keys.length != 32) {
847 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
848 "Invalid key length, expected 32 got %d", keys.length);
852 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
854 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
855 "Failed to import sealing key!");
860 #ifdef HAVE_CRED_STORE
862 #define MAX_CRED_OPTIONS 10
864 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
867 struct mag_config *cfg = (struct mag_config *)mconfig;
868 gss_key_value_element_desc *elements;
877 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
878 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
882 key = apr_pstrndup(parms->pool, w, (p-w));
883 value = apr_pstrdup(parms->pool, p + 1);
885 if (!cfg->cred_store) {
886 cfg->cred_store = apr_pcalloc(parms->pool,
887 sizeof(gss_key_value_set_desc));
888 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
889 cfg->cred_store->elements = apr_palloc(parms->pool, size);
892 elements = cfg->cred_store->elements;
893 count = cfg->cred_store->count;
895 if (count >= MAX_CRED_OPTIONS) {
896 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
897 "Too many GssapiCredStore options (MAX: %d)",
901 cfg->cred_store->count++;
903 elements[count].key = key;
904 elements[count].value = value;
909 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
912 struct mag_config *cfg = (struct mag_config *)mconfig;
914 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
920 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
922 struct mag_config *cfg = (struct mag_config *)mconfig;
924 cfg->use_basic_auth = on ? true : false;
928 #define MAX_ALLOWED_MECHS 10
930 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
933 struct mag_config *cfg = (struct mag_config *)mconfig;
937 if (!cfg->allowed_mechs) {
938 cfg->allowed_mechs = apr_pcalloc(parms->pool,
939 sizeof(gss_OID_set_desc));
940 size = sizeof(gss_OID) * MAX_ALLOWED_MECHS;
941 cfg->allowed_mechs->elements = apr_palloc(parms->pool, size);
944 if (strcmp(w, "krb5") == 0) {
946 } else if (strcmp(w, "iakerb") == 0) {
947 oid = gss_mech_iakerb;
948 } else if (strcmp(w, "ntlmssp") == 0) {
949 oid = &gss_mech_ntlmssp;
951 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
952 "Unrecognized GSSAPI Mechanism: %s", w);
956 if (cfg->allowed_mechs->count >= MAX_ALLOWED_MECHS) {
957 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
958 "Too many GssapiAllowedMech options (MAX: %d)",
962 cfg->allowed_mechs->elements[cfg->allowed_mechs->count] = *oid;
963 cfg->allowed_mechs->count++;
968 static const command_rec mag_commands[] = {
969 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
970 "Work only if connection is SSL Secured"),
971 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
972 "Translate principals to local names"),
973 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
974 "Authentication is bound to the TCP connection"),
975 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
976 "Send Persitent-Auth header according to connection bound"),
977 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
978 "Authentication uses mod_sessions to hold status"),
979 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
980 "Key Used to seal session data."),
981 #ifdef HAVE_CRED_STORE
982 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
983 "Initializes credentials for s4u2proxy usage"),
984 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
986 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
987 OR_AUTHCFG, "Directory to store delegated credentials"),
989 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
990 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
991 "Allows use of Basic Auth for authentication"),
993 AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
994 "Allowed Mechanisms"),
999 mag_register_hooks(apr_pool_t *p)
1001 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
1002 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1003 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
1006 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
1008 STANDARD20_MODULE_STUFF,
1009 mag_create_dir_config,