Skip to content

Commit 996cc08

Browse files
committed
deprecate OIDCHTMLErrorTemplate
rely on standard Apache error handling capabilities; by default environment variable strings REDIRECT_OIDC_ERROR and REDIRECT_OIDC_ERROR_DESC are available in ErrorDocument backwards compatibility is retained by setting "OIDCHTMLErrorTemplate deprecated"; bump to 2.4.14rc0 Signed-off-by: Hans Zandbelt <[email protected]>
1 parent ee4efbe commit 996cc08

File tree

6 files changed

+147
-82
lines changed

6 files changed

+147
-82
lines changed

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
04/07/2023
2+
- deprecate OIDCHTMLErrorTemplate and rely on standard Apache error handling capabilities by default
3+
environment variable strings REDIRECT_OIDC_ERROR and REDIRECT_OIDC_ERROR_DESC are available in ErrorDocument
4+
backwards compatibility is retained by setting "OIDCHTMLErrorTemplate deprecated"
5+
- bump to 2.4.14rc0
6+
17
04/07/2023
28
- add support for passing on claims resolved from the userinfo endpoint in a JWT signed by
39
mod_auth_openidc using `OIDCPassUserInfoAs signed_jwt[:<name>]` with the keys configured

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
AC_INIT([mod_auth_openidc],[2.4.13.3rc3],[[email protected]])
1+
AC_INIT([mod_auth_openidc],[2.4.14rc0],[[email protected]])
22

33
AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
44

src/config.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2150,6 +2150,15 @@ int oidc_cfg_session_cache_fallback_to_cookie(request_rec *r) {
21502150
return cfg->session_cache_fallback_to_cookie;
21512151
}
21522152

2153+
static const char* oidc_set_html_error_template(cmd_parms *cmd,
2154+
void *struct_ptr, const char *arg) {
2155+
oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
2156+
&auth_openidc_module);
2157+
oidc_swarn(cmd->server,
2158+
OIDCHTMLErrorTemplate" is deprecated; please use the standard Apache features to deal with the "OIDC_ERROR_ENVVAR" and "OIDC_ERROR_DESC_ENVVAR" environment variables set by this module, see: https://httpd.apache.org/docs/2.4/custom-error.html");
2159+
return ap_set_string_slot(cmd, cfg, arg);
2160+
}
2161+
21532162
/*
21542163
* create a new directory config record with defaults
21552164
*/
@@ -3550,7 +3559,7 @@ const command_rec oidc_config_cmds[] = {
35503559
"Timeout waiting for a response of the Redis servers."),
35513560
#endif
35523561
AP_INIT_TAKE1(OIDCHTMLErrorTemplate,
3553-
oidc_set_string_slot,
3562+
oidc_set_html_error_template,
35543563
(void*)APR_OFFSETOF(oidc_cfg, error_template),
35553564
RSRC_CONF,
35563565
"Name of a HTML error template: needs to contain two \"%s\" characters, one for the error message, one for the description."),

src/mod_auth_openidc.c

Lines changed: 85 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,8 @@ static int oidc_clean_expired_state_cookies(request_rec *r, oidc_cfg *c,
611611
/*
612612
* restore the state that was maintained between authorization request and response in an encrypted cookie
613613
*/
614-
static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c, const char *state,
615-
oidc_proto_state_t **proto_state) {
614+
static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c,
615+
const char *state, oidc_proto_state_t **proto_state) {
616616

617617
oidc_debug(r, "enter");
618618

@@ -624,12 +624,15 @@ static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c, const ch
624624
/* get the state cookie value first */
625625
char *cookieValue = oidc_util_get_cookie(r, cookieName);
626626
if (cookieValue == NULL) {
627-
oidc_error(r, "no \"%s\" state cookie found: check domain and samesite cookie settings", cookieName);
627+
oidc_error(r,
628+
"no \"%s\" state cookie found: check domain and samesite cookie settings",
629+
cookieName);
628630
return FALSE;
629631
}
630632

631633
/* clear state cookie because we don't need it anymore */
632-
oidc_util_set_cookie(r, cookieName, "", 0, OIDC_COOKIE_EXT_SAME_SITE_NONE(c, r));
634+
oidc_util_set_cookie(r, cookieName, "", 0,
635+
OIDC_COOKIE_EXT_SAME_SITE_NONE(c, r));
633636

634637
*proto_state = oidc_proto_state_from_cookie(r, c, cookieValue);
635638
if (*proto_state == NULL)
@@ -641,7 +644,9 @@ static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c, const ch
641644
char *calc = oidc_get_browser_state_hash(r, c, nonce);
642645
/* compare the calculated hash with the value provided in the authorization response */
643646
if (_oidc_strcmp(calc, state) != 0) {
644-
oidc_error(r, "calculated state from cookie does not match state parameter passed back in URL: \"%s\" != \"%s\"", state, calc);
647+
oidc_error(r,
648+
"calculated state from cookie does not match state parameter passed back in URL: \"%s\" != \"%s\"",
649+
state, calc);
645650
oidc_proto_state_destroy(*proto_state);
646651
return FALSE;
647652
}
@@ -652,14 +657,14 @@ static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c, const ch
652657
if (apr_time_now() > ts + apr_time_from_sec(c->state_timeout)) {
653658
oidc_error(r, "state has expired");
654659
if ((c->default_sso_url == NULL)
655-
|| (apr_table_get(r->subprocess_env, "OIDC_NO_DEFAULT_URL_ON_STATE_TIMEOUT") != NULL)) {
656-
oidc_util_html_send_error(r, c->error_template, "Invalid Authentication Response", apr_psprintf(r->pool, "This is due to a timeout; please restart your authentication session by re-entering the URL/bookmark you originally wanted to access: %s", oidc_proto_state_get_original_url(*proto_state)),
657-
OK);
658-
/*
659-
* a hack for Apache 2.4 to prevent it from writing its own 500/400/302 HTML document
660-
* text by making ap_send_error_response in http_protocol.c return early...
661-
*/
662-
r->header_only = 1;
660+
|| (apr_table_get(r->subprocess_env,
661+
"OIDC_NO_DEFAULT_URL_ON_STATE_TIMEOUT") != NULL)) {
662+
oidc_util_html_send_error(r, c->error_template,
663+
"Invalid Authentication Response",
664+
apr_psprintf(r->pool,
665+
"This is due to a timeout; please restart your authentication session by re-entering the URL/bookmark you originally wanted to access: %s",
666+
oidc_proto_state_get_original_url(*proto_state)),
667+
OK);
663668
}
664669
oidc_proto_state_destroy(*proto_state);
665670
return FALSE;
@@ -669,7 +674,8 @@ static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c, const ch
669674
oidc_proto_state_set_state(*proto_state, state);
670675

671676
/* log the restored state object */
672-
oidc_debug(r, "restored state: %s", oidc_proto_state_to_string(r, *proto_state));
677+
oidc_debug(r, "restored state: %s",
678+
oidc_proto_state_to_string(r, *proto_state));
673679

674680
/* we've made it */
675681
return TRUE;
@@ -1997,8 +2003,8 @@ static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
19972003

19982004
/* match the returned state parameter against the state stored in the browser */
19992005
if (oidc_authorization_response_match_state(r, c,
2000-
apr_table_get(params, OIDC_PROTO_STATE), &provider, &proto_state)
2001-
== FALSE) {
2006+
apr_table_get(params, OIDC_PROTO_STATE), &provider,
2007+
&proto_state) == FALSE) {
20022008
if (c->default_sso_url != NULL) {
20032009
oidc_warn(r,
20042010
"invalid authorization response state; a default SSO URL is set, sending the user there: %s",
@@ -2009,11 +2015,21 @@ static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
20092015
}
20102016
oidc_error(r,
20112017
"invalid authorization response state and no default SSO URL is set, sending an error...");
2012-
// if content was already returned via html/http send then don't return 500
2013-
// but send 200 to avoid extraneous internal error document text to be sent
2014-
return ((r->user) && (_oidc_strncmp(r->user, "", 1) == 0)) ?
2015-
OK :
2016-
HTTP_BAD_REQUEST;
2018+
2019+
// detect if we've set a (timeout) error message in oidc_authorization_response_match_state
2020+
if (apr_table_get(r->subprocess_env, OIDC_ERROR_ENVVAR) != NULL) {
2021+
// overrides the OK from the timeout detection
2022+
return HTTP_BAD_REQUEST;
2023+
} else if (c->error_template) {
2024+
// for backwards compatibility
2025+
if ((r->user) && (_oidc_strncmp(r->user, "", 1) == 0))
2026+
return HTTP_BAD_REQUEST;
2027+
}
2028+
2029+
return oidc_util_html_send_error(r, c->error_template,
2030+
"Invalid Authorization Response",
2031+
"Could not match the authorization response to an earlier request via the state parameter and corresponding state cookie",
2032+
HTTP_BAD_REQUEST);
20172033
}
20182034

20192035
/* see if the response is an error response */
@@ -2088,7 +2104,8 @@ static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
20882104
return HTTP_INTERNAL_SERVER_ERROR;
20892105
}
20902106

2091-
oidc_debug(r, "set remote_user to \"%s\" in new session \"%s\"", r->user, session->uuid);
2107+
oidc_debug(r, "set remote_user to \"%s\" in new session \"%s\"",
2108+
r->user, session->uuid);
20922109

20932110
} else {
20942111
oidc_error(r, "remote user could not be set");
@@ -4186,57 +4203,71 @@ static authz_status oidc_handle_unauthorized_user24(request_rec *r) {
41864203

41874204
oidc_debug(r, "enter");
41884205

4189-
oidc_cfg *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
4206+
oidc_cfg *c = ap_get_module_config(r->server->module_config,
4207+
&auth_openidc_module);
41904208
char *html_head = NULL;
41914209

41924210
if (apr_strnatcasecmp((const char*) ap_auth_type(r),
4193-
OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0) {
4194-
oidc_debug(r, "setting environment variable %s to \"%s\" for usage in mod_headers", OIDC_OAUTH_BEARER_SCOPE_ERROR, OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE);
4195-
apr_table_set(r->subprocess_env, OIDC_OAUTH_BEARER_SCOPE_ERROR, OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE);
4211+
OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0) {
4212+
oidc_debug(r,
4213+
"setting environment variable %s to \"%s\" for usage in mod_headers",
4214+
OIDC_OAUTH_BEARER_SCOPE_ERROR,
4215+
OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE);
4216+
apr_table_set(r->subprocess_env, OIDC_OAUTH_BEARER_SCOPE_ERROR,
4217+
OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE);
41964218
return AUTHZ_DENIED;
41974219
}
41984220

41994221
/* see if we've configured OIDCUnAutzAction for this path */
42004222
switch (oidc_dir_cfg_unautz_action(r)) {
4201-
case OIDC_UNAUTZ_RETURN403:
4202-
case OIDC_UNAUTZ_RETURN401:
4203-
if (oidc_dir_cfg_unauthz_arg(r)) {
4204-
oidc_util_html_send(r, "Authorization Error", NULL, NULL, oidc_dir_cfg_unauthz_arg(r),
4205-
HTTP_UNAUTHORIZED);
4206-
r->header_only = 1;
4207-
}
4208-
return AUTHZ_DENIED;
4209-
case OIDC_UNAUTZ_RETURN302:
4210-
html_head =
4211-
apr_psprintf(r->pool, "<meta http-equiv=\"refresh\" content=\"0; url=%s\">", oidc_dir_cfg_unauthz_arg(r));
4212-
oidc_util_html_send(r, "Authorization Error Redirect", html_head, NULL, NULL,
4223+
case OIDC_UNAUTZ_RETURN403:
4224+
case OIDC_UNAUTZ_RETURN401:
4225+
if (oidc_dir_cfg_unauthz_arg(r)) {
4226+
oidc_util_html_send_error(r, c->error_template,
4227+
"Authorization Error", oidc_dir_cfg_unauthz_arg(r),
42134228
HTTP_UNAUTHORIZED);
4214-
r->header_only = 1;
4229+
if (c->error_template)
4230+
r->header_only = 1;
4231+
}
4232+
return AUTHZ_DENIED;
4233+
case OIDC_UNAUTZ_RETURN302:
4234+
html_head = apr_psprintf(r->pool,
4235+
"<meta http-equiv=\"refresh\" content=\"0; url=%s\">",
4236+
oidc_dir_cfg_unauthz_arg(r));
4237+
oidc_util_html_send(r, "Authorization Error Redirect", html_head, NULL,
4238+
NULL,
4239+
HTTP_UNAUTHORIZED);
4240+
r->header_only = 1;
4241+
return AUTHZ_DENIED;
4242+
case OIDC_UNAUTZ_AUTHENTICATE:
4243+
/*
4244+
* exception handling: if this looks like an HTTP request that cannot
4245+
* complete an authentication round trip to the provider, we
4246+
* won't redirect the user and thus avoid creating a state cookie
4247+
*/
4248+
if (oidc_is_auth_capable_request(r) == FALSE)
42154249
return AUTHZ_DENIED;
4216-
case OIDC_UNAUTZ_AUTHENTICATE:
4217-
/*
4218-
* exception handling: if this looks like an HTTP request that cannot
4219-
* complete an authentication round trip to the provider, we
4220-
* won't redirect the user and thus avoid creating a state cookie
4221-
*/
4222-
if (oidc_is_auth_capable_request(r) == FALSE)
4223-
return AUTHZ_DENIED;
4224-
break;
4250+
break;
42254251
}
42264252

4227-
oidc_authenticate_user(r, c, NULL, oidc_get_current_url(r, c->x_forwarded_headers), NULL,
4228-
NULL, NULL, oidc_dir_cfg_path_auth_request_params(r), oidc_dir_cfg_path_scope(r));
4253+
oidc_authenticate_user(r, c, NULL,
4254+
oidc_get_current_url(r, c->x_forwarded_headers), NULL,
4255+
NULL, NULL, oidc_dir_cfg_path_auth_request_params(r),
4256+
oidc_dir_cfg_path_scope(r));
42294257

42304258
const char *location = oidc_util_hdr_out_location_get(r);
42314259

4232-
if ((oidc_request_state_get(r, OIDC_REQUEST_STATE_KEY_DISCOVERY) != NULL) && (location == NULL))
4260+
if ((oidc_request_state_get(r, OIDC_REQUEST_STATE_KEY_DISCOVERY) != NULL)
4261+
&& (location == NULL))
42334262
return AUTHZ_GRANTED;
42344263

42354264
if (location != NULL) {
4236-
oidc_debug(r, "send HTML refresh with authorization redirect: %s", location);
4265+
oidc_debug(r, "send HTML refresh with authorization redirect: %s",
4266+
location);
42374267

4238-
html_head =
4239-
apr_psprintf(r->pool, "<meta http-equiv=\"refresh\" content=\"0; url=%s\">", location);
4268+
html_head = apr_psprintf(r->pool,
4269+
"<meta http-equiv=\"refresh\" content=\"0; url=%s\">",
4270+
location);
42404271
oidc_util_html_send(r, "Stepup Authentication", html_head, NULL, NULL,
42414272
HTTP_UNAUTHORIZED);
42424273
/*

src/mod_auth_openidc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ APLOG_USE_MODULE(auth_openidc);
248248
#define OIDC_COOKIE_SAMESITE_LAX(c, r) \
249249
c->cookie_same_site ? OIDC_COOKIE_EXT_SAME_SITE_LAX : OIDC_COOKIE_EXT_SAME_SITE_NONE(c, r)
250250

251+
#define OIDC_ERROR_ENVVAR "OIDC_ERROR"
252+
#define OIDC_ERROR_DESC_ENVVAR "OIDC_ERROR_DESC"
253+
251254
/* https://tools.ietf.org/html/draft-ietf-tokbind-ttrp-01 */
252255
#define OIDC_TB_CFG_PROVIDED_ENV_VAR "Sec-Provided-Token-Binding-ID"
253256
/* https://www.ietf.org/id/draft-ietf-oauth-mtls-12 */

src/util.c

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,39 +1735,55 @@ int oidc_util_html_send_error(request_rec *r, const char *html_template,
17351735

17361736
if (html_template != NULL) {
17371737

1738-
html_template = oidc_util_get_full_path(r->pool, html_template);
1739-
1740-
if (html_error_template_contents == NULL) {
1741-
int rc = oidc_util_file_read(r, html_template,
1742-
r->server->process->pool, &html_error_template_contents);
1743-
if (rc == FALSE) {
1744-
oidc_error(r, "could not read HTML error template: %s",
1745-
html_template);
1746-
html_error_template_contents = NULL;
1738+
if (_oidc_strcmp(html_template, "deprecated") != 0) {
1739+
1740+
html_template = oidc_util_get_full_path(r->pool, html_template);
1741+
1742+
if (html_error_template_contents == NULL) {
1743+
int rc = oidc_util_file_read(r, html_template,
1744+
r->server->process->pool,
1745+
&html_error_template_contents);
1746+
if (rc == FALSE) {
1747+
oidc_error(r, "could not read HTML error template: %s",
1748+
html_template);
1749+
html_error_template_contents = NULL;
1750+
}
17471751
}
1748-
}
17491752

1750-
if (html_error_template_contents) {
1751-
html = apr_psprintf(r->pool, html_error_template_contents,
1752-
oidc_util_html_escape(r->pool, error ? error : ""),
1753-
oidc_util_html_escape(r->pool,
1754-
description ? description : ""));
1753+
if (html_error_template_contents) {
1754+
html = apr_psprintf(r->pool, html_error_template_contents,
1755+
oidc_util_html_escape(r->pool, error ? error : ""),
1756+
oidc_util_html_escape(r->pool,
1757+
description ? description : ""));
17551758

1756-
return oidc_util_http_send(r, html, _oidc_strlen(html),
1757-
OIDC_CONTENT_TYPE_TEXT_HTML, status_code);
1759+
return oidc_util_http_send(r, html, _oidc_strlen(html),
1760+
OIDC_CONTENT_TYPE_TEXT_HTML, status_code);
1761+
}
17581762
}
1759-
}
17601763

1761-
if (error != NULL) {
1762-
html = apr_psprintf(r->pool, "%s<p>Error: <pre>%s</pre></p>", html,
1763-
oidc_util_html_escape(r->pool, error));
1764-
}
1765-
if (description != NULL) {
1766-
html = apr_psprintf(r->pool, "%s<p>Description: <pre>%s</pre></p>",
1767-
html, oidc_util_html_escape(r->pool, description));
1764+
if (error != NULL) {
1765+
html = apr_psprintf(r->pool, "%s<p>Error: <pre>%s</pre></p>", html,
1766+
oidc_util_html_escape(r->pool, error));
1767+
}
1768+
if (description != NULL) {
1769+
html = apr_psprintf(r->pool, "%s<p>Description: <pre>%s</pre></p>",
1770+
html, oidc_util_html_escape(r->pool, description));
1771+
}
1772+
1773+
return oidc_util_html_send(r, "Error", NULL, NULL, html, status_code);
17681774
}
17691775

1770-
return oidc_util_html_send(r, "Error", NULL, NULL, html, status_code);
1776+
oidc_debug(r, "setting "OIDC_ERROR_ENVVAR" environment variable to: %s",
1777+
error);
1778+
apr_table_set(r->subprocess_env, OIDC_ERROR_ENVVAR, error ? error : "");
1779+
1780+
oidc_debug(r,
1781+
"setting "OIDC_ERROR_DESC_ENVVAR" environment variable to: %s",
1782+
description);
1783+
apr_table_set(r->subprocess_env, OIDC_ERROR_DESC_ENVVAR,
1784+
description ? description : "");
1785+
1786+
return status_code;
17711787
}
17721788

17731789
/*

0 commit comments

Comments
 (0)