Hello community,
here is the log from the commit of package apache2-mod_auth_openidc for
openSUSE:Factory checked in at 2020-08-12 10:28:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/apache2-mod_auth_openidc (Old)
and /work/SRC/openSUSE:Factory/.apache2-mod_auth_openidc.new.3399 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "apache2-mod_auth_openidc"
Wed Aug 12 10:28:26 2020 rev:10 rq:825751 version:2.4.3
Changes:
--------
---
/work/SRC/openSUSE:Factory/apache2-mod_auth_openidc/apache2-mod_auth_openidc.changes
2020-03-25 23:49:02.792040419 +0100
+++
/work/SRC/openSUSE:Factory/.apache2-mod_auth_openidc.new.3399/apache2-mod_auth_openidc.changes
2020-08-12 10:29:52.412028787 +0200
@@ -1,0 +2,17 @@
+Tue Aug 11 08:20:49 UTC 2020 - Michael Ströder <[email protected]>
+
+- Update to version 2.4.3
+ * Bugfixes
+ - prevent open redirect on refresh token requests
+ - add new OIDCRedirectURLsAllowed primitive to handle post logout
+ and refresh-return-to validation
+ addresses #453; closes #466
+ - when stripping cookies, add a space between cookies in the resulting
header (required by RFC 6265)
+ - fix compilation against Apache 2.0
+ * Features
+ - add OIDCStateInputHeaders that allows configuring the header values
+ used to calculate the fingerprint of the state during authentication
+ - added OIDCValidateIssuer primitive to allow for disabling of issuer
+ matching, helps to support multi-tenant applications i.e. Microsoft AAD
+
+-------------------------------------------------------------------
Old:
----
apache2-mod_auth_openidc-2.4.2.1.tar.gz
New:
----
apache2-mod_auth_openidc-2.4.3.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ apache2-mod_auth_openidc.spec ++++++
--- /var/tmp/diff_new_pack.QO0WBj/_old 2020-08-12 10:29:54.040030044 +0200
+++ /var/tmp/diff_new_pack.QO0WBj/_new 2020-08-12 10:29:54.040030044 +0200
@@ -19,7 +19,7 @@
%define apxs %{_sbindir}/apxs2
%define apache_libexecdir %(%{apxs} -q LIBEXECDIR)
Name: apache2-mod_auth_openidc
-Version: 2.4.2.1
+Version: 2.4.3
Release: 0
Summary: Apache2.x module for an OpenID Connect enabled Identity
Provider
License: Apache-2.0
++++++ apache2-mod_auth_openidc-2.4.2.1.tar.gz ->
apache2-mod_auth_openidc-2.4.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/AUTHORS
new/mod_auth_openidc-2.4.3/AUTHORS
--- old/mod_auth_openidc-2.4.2.1/AUTHORS 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/AUTHORS 2020-06-10 18:14:24.000000000 +0200
@@ -61,3 +61,4 @@
Paolo Battino
absynth76 <https://github.com/absynth76>
Aaron Jones <https://github.com/wwaaron>
+ Bryan Ingram <https://github/bcingram>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/ChangeLog
new/mod_auth_openidc-2.4.3/ChangeLog
--- old/mod_auth_openidc-2.4.2.1/ChangeLog 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/ChangeLog 2020-06-10 18:14:24.000000000
+0200
@@ -1,3 +1,26 @@
+06/10/2020
+- prevent open redirect on refresh token requests
+ add new OIDCRedirectURLsAllowed primitive to handle post logout and
refresh-return-to validation
+ addresses #453; closes #466
+- release 2.4.3
+
+06/09/2020
+- fix complex expressions crash when compiled from source with libjq; closes
#472
+ thanks vincentscharf0803
+ introduced by OIDCStateInputHeaders addition in 2.4.3rc0
+- bump to 2.4.3rc1
+
+05/11/2020
+- added OIDCValidateIssuer to allow for disabling of issuer matching. helps
to support multi-tenant applications.
+
+05/02/2020
+- when stripping cookies, add a space between cookies in the resulting header
(required by RFC 6265)
+- move oidc_parse_config inside MODULE_MAGIC_NUMBER_MAJOR to make sure the
module compiles with Apache 2.0
+
+04/25/2020
+- add OIDCStateInputHeaders that allows configuring the header values used to
calculate the fingerprint of the state during authentication
+- bump to 2.4.3rc0
+
03/25/2020
- oops: fix json_deep_copy of claims
- release 2.4.2.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/auth_openidc.conf
new/mod_auth_openidc-2.4.3/auth_openidc.conf
--- old/mod_auth_openidc-2.4.2.1/auth_openidc.conf 2020-03-25
13:09:26.000000000 +0100
+++ new/mod_auth_openidc-2.4.3/auth_openidc.conf 2020-06-10
18:14:24.000000000 +0200
@@ -137,6 +137,11 @@
# NB: this can be overridden on a per-OP basis in the .conf file using the
key: ssl_validate_server
#OIDCSSLValidateServer [On|Off]
+# Require configured issuer to match the issuer returned in id_token.
+# (Disable to support Azure AD multi-tenant applications.)
+# When not defined, the default value is "On".
+#OIDCValidateIssuer [On|Off]
+
# The refresh interval in seconds for the claims obtained from the userinfo
endpoint
# When not defined the default is 0, i.e. the claims are retrieved only at
session creation time.
# NB: this can be overridden on a per-OP basis in the .conf file using the
key: userinfo_refresh_interval
@@ -823,3 +828,17 @@
# When not defined no attempt is made to refresh the access token (unless
implicitly with OIDCUserInfoRefreshInterval)
# The optional logout_on_error flag makes the refresh logout the current local
session if the refresh fails.
#OIDCRefreshAccessTokenBeforeExpiry <seconds> [logout_on_error]
+
+# Defines whether the value of the User-Agent and X-Forwarded-For headers will
be used as the input
+# for calculating the fingerprint of the state during authentication.
+# When not defined the default "both" is used.
+#OIDCStateInputHeaders [none|user-agent|x-forwarded-for|both]
+
+# Define one or more regular expressions that specify URLs (or domains)
allowed for post logout and
+# other redirects such as the "return_to" value on refresh token requests,
e.g.:
+# OIDCRedirectURLsAllowed ^https://www.example.com
^https://(\w+).example.org ^https://example.net/app
+# or:
+# OIDCRedirectURLsAllowed ^https://www.example.com/logout$
^https://www.example.com/app/return_to$
+# When not defined, the default is to match the hostname in the URL redirected
to against
+# the hostname in the current request.
+#OIDCRedirectURLsAllowed [<regexp>]+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/configure.ac
new/mod_auth_openidc-2.4.3/configure.ac
--- old/mod_auth_openidc-2.4.2.1/configure.ac 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/configure.ac 2020-06-10 18:14:24.000000000
+0200
@@ -1,4 +1,4 @@
-AC_INIT([mod_auth_openidc],[2.4.2.1],[[email protected]])
+AC_INIT([mod_auth_openidc],[2.4.3],[[email protected]])
AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/authz.c
new/mod_auth_openidc-2.4.3/src/authz.c
--- old/mod_auth_openidc-2.4.2.1/src/authz.c 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/authz.c 2020-06-10 18:14:24.000000000
+0200
@@ -436,10 +436,14 @@
if (!claims)
return AUTHZ_DENIED;
- t = ap_expr_str_exec(r, expr, &err);
- if (err) {
- oidc_error(r, "could not evaluate expression '%s': %s",
require_args, err);
- return AUTHZ_DENIED;
+ if (expr) {
+ t = ap_expr_str_exec(r, expr, &err);
+ if (err) {
+ oidc_error(r, "could not evaluate expression '%s': %s",
require_args, err);
+ return AUTHZ_DENIED;
+ }
+ } else {
+ t = require_args;
}
/* loop over the Required specifications */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/config.c
new/mod_auth_openidc-2.4.3/src/config.c
--- old/mod_auth_openidc-2.4.2.1/src/config.c 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/config.c 2020-06-10 18:14:24.000000000
+0200
@@ -76,6 +76,8 @@
/* validate SSL server certificates by default */
#define OIDC_DEFAULT_SSL_VALIDATE_SERVER 1
+/* validate issuer by default */
+#define OIDC_DEFAULT_VALIDATE_ISSUER 1
/* default scope requested from the OP */
#define OIDC_DEFAULT_SCOPE "openid"
/* default claim delimiter for multi-valued claims passed in a HTTP header */
@@ -168,6 +170,8 @@
#define OIDC_DEFAULT_PROVIDER_ISSUER_SPECIFIC_REDIRECT_URI 0
/* define the default number of seconds that the access token needs to be
valid for; -1 = no refresh */
#define OIDC_DEFAULT_REFRESH_ACCESS_TOKEN_BEFORE_EXPIRY -1
+/* default setting for calculating the fingerprint of the state from request
headers during authentication */
+#define OIDC_DEFAULT_STATE_INPUT_HEADERS (OIDC_STATE_INPUT_HEADERS_USER_AGENT
| OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR)
#define OIDCProviderMetadataURL "OIDCProviderMetadataURL"
#define OIDCProviderIssuer "OIDCProviderIssuer"
@@ -195,6 +199,7 @@
#define OIDCUserInfoTokenMethod "OIDCUserInfoTokenMethod"
#define OIDCTokenBindingPolicy "OIDCTokenBindingPolicy"
#define OIDCSSLValidateServer "OIDCSSLValidateServer"
+#define OIDCValidateIssuer "OIDCValidateIssuer"
#define OIDCClientName "OIDCClientName"
#define OIDCClientContact "OIDCClientContact"
#define OIDCScope "OIDCScope"
@@ -267,6 +272,8 @@
#define OIDCOAuthServerMetadataURL "OIDCOAuthServerMetadataURL"
#define OIDCOAuthAccessTokenBindingPolicy
"OIDCOAuthAccessTokenBindingPolicy"
#define OIDCRefreshAccessTokenBeforeExpiry
"OIDCRefreshAccessTokenBeforeExpiry"
+#define OIDCStateInputHeaders "OIDCStateInputHeaders"
+#define OIDCRedirectURLsAllowed "OIDCRedirectURLsAllowed"
extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
@@ -466,6 +473,20 @@
}
/*
+ * set validate issuer slot
+ */
+static const char *oidc_set_validate_issuer_slot(cmd_parms *cmd,
+ void *struct_ptr, const char *arg) {
+ oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
+ cmd->server->module_config, &auth_openidc_module);
+ int b = 0;
+ const char *rv = oidc_parse_boolean(cmd->pool, arg, &b);
+ if (rv == NULL)
+ rv = ap_set_flag_slot(cmd, cfg, b);
+ return OIDC_CONFIG_DIR_RV(cmd, rv);
+}
+
+/*
* return the right token endpoint authentication method validation function,
based on whether private keys are set
*/
oidc_valid_function_t oidc_cfg_get_valid_endpoint_auth_function(oidc_cfg *cfg)
{
@@ -1082,17 +1103,40 @@
const char *rv1 =
oidc_parse_refresh_access_token_before_expiry(cmd->pool,
arg1, &dir_cfg->refresh_access_token_before_expiry);
if (rv1 != NULL)
- return apr_psprintf(cmd->pool, "Invalid value for directive
'%s': %s", cmd->directive->directive, rv1);
+ return apr_psprintf(cmd->pool, "Invalid value for directive
'%s': %s",
+ cmd->directive->directive, rv1);
if (arg2) {
- const char *rv2 =
oidc_parse_logout_on_error_refresh_as(cmd->pool,
- arg2, &dir_cfg->logout_on_error_refresh);
+ const char *rv2 =
oidc_parse_logout_on_error_refresh_as(cmd->pool, arg2,
+ &dir_cfg->logout_on_error_refresh);
return OIDC_CONFIG_DIR_RV(cmd, rv2);
}
return NULL;
}
+/*
+ * define which header we use for calculating the fingerprint of the state
during authentication
+ */
+static const char * oidc_set_state_input_headers_as(cmd_parms *cmd, void *m,
+ const char *arg) {
+ oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
+ cmd->server->module_config, &auth_openidc_module);
+ const char *rv = oidc_parse_set_state_input_headers_as(cmd->pool, arg,
+ &cfg->state_input_headers);
+ return OIDC_CONFIG_DIR_RV(cmd, rv);
+}
+
+static const char * oidc_set_redirect_urls_allowed(cmd_parms *cmd, void *m,
+ const char *arg) {
+ oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
+ cmd->server->module_config, &auth_openidc_module);
+ if (cfg->redirect_urls_allowed == NULL)
+ cfg->redirect_urls_allowed = apr_hash_make(cmd->pool);
+ apr_hash_set(cfg->redirect_urls_allowed, arg, APR_HASH_KEY_STRING, arg);
+ return NULL;
+}
+
int oidc_cfg_dir_refresh_access_token_before_expiry(request_rec *r) {
oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
&auth_openidc_module);
@@ -1130,6 +1174,7 @@
provider->backchannel_logout_supported = OIDC_CONFIG_POS_INT_UNSET;
provider->ssl_validate_server = OIDC_DEFAULT_SSL_VALIDATE_SERVER;
+ provider->validate_issuer = OIDC_DEFAULT_VALIDATE_ISSUER;
provider->client_name = OIDC_DEFAULT_CLIENT_NAME;
provider->client_contact = NULL;
provider->registration_token = NULL;
@@ -1268,6 +1313,10 @@
c->provider.issuer_specific_redirect_uri =
OIDC_DEFAULT_PROVIDER_ISSUER_SPECIFIC_REDIRECT_URI;
+ c->state_input_headers = OIDC_DEFAULT_STATE_INPUT_HEADERS;
+
+ c->redirect_urls_allowed = NULL;
+
return c;
}
@@ -1371,6 +1420,10 @@
!= OIDC_DEFAULT_SSL_VALIDATE_SERVER ?
add->provider.ssl_validate_server :
base->provider.ssl_validate_server;
+ c->provider.validate_issuer =
+ add->provider.validate_issuer !=
OIDC_DEFAULT_VALIDATE_ISSUER ?
+ add->provider.validate_issuer :
+ base->provider.validate_issuer;
c->provider.client_name =
apr_strnatcmp(add->provider.client_name,
OIDC_DEFAULT_CLIENT_NAME)
!= 0 ?
@@ -1731,6 +1784,14 @@
add->provider.issuer_specific_redirect_uri :
base->provider.issuer_specific_redirect_uri;
+ c->state_input_headers =
+ add->state_input_headers !=
OIDC_DEFAULT_STATE_INPUT_HEADERS ?
+ add->state_input_headers :
base->state_input_headers;
+
+ c->redirect_urls_allowed =
+ add->redirect_urls_allowed != NULL ?
+ add->redirect_urls_allowed :
base->redirect_urls_allowed;
+
return c;
}
@@ -2359,29 +2420,26 @@
return oidc_config_check_merged_vhost_configs(pool, s);
}
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
static const char *oidc_parse_config(cmd_parms *cmd, const char *require_line,
const void **parsed_require_line) {
const char *expr_err = NULL;
ap_expr_info_t *expr;
expr = ap_expr_parse_cmd(cmd, require_line, AP_EXPR_FLAG_STRING_RESULT,
- &expr_err, NULL);
+ &expr_err, NULL);
if (expr_err)
return apr_pstrcat(cmd->temp_pool,
- "Cannot parse expression in require line: ",
- expr_err, NULL);
+ "Cannot parse expression in require line: ",
expr_err, NULL);
*parsed_require_line = expr;
return NULL;
}
-#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
static const authz_provider oidc_authz_claim_provider = {
- &oidc_authz_checker_claim,
- &oidc_parse_config,
-};
+ &oidc_authz_checker_claim, &oidc_parse_config, };
#ifdef USE_LIBJQ
static const authz_provider oidc_authz_claims_expr_provider = {
&oidc_authz_checker_claims_expr,
@@ -2671,6 +2729,11 @@
(void*)APR_OFFSETOF(oidc_cfg,
provider.ssl_validate_server),
RSRC_CONF,
"Require validation of the OpenID Connect OP
SSL server certificate for successful authentication (On or Off)"),
+ AP_INIT_TAKE1(OIDCValidateIssuer,
+ oidc_set_validate_issuer_slot,
+ (void*)APR_OFFSETOF(oidc_cfg,
provider.validate_issuer),
+ RSRC_CONF,
+ "Require validation of token issuer for
successful authentication (On or Off)"),
AP_INIT_TAKE1(OIDCClientName,
oidc_set_string_slot,
(void *) APR_OFFSETOF(oidc_cfg,
provider.client_name),
@@ -3111,5 +3174,17 @@
RSRC_CONF|ACCESS_CONF|OR_AUTHCFG,
"Ensure the access token is valid for at least
<x> seconds by refreshing it if required; must be: <x> [logout_on_error]; the
logout_on_error performs a logout on refresh error."),
+ AP_INIT_TAKE1(OIDCStateInputHeaders,
+ oidc_set_state_input_headers_as,
+ NULL,
+ RSRC_CONF,
+ "Specify header name which is used as the input
for calculating the fingerprint of the state during authentication; must be one
of \"none\", \"user-agent\", \"x-forwarded-for\" or \"both\" (default)."),
+
+ AP_INIT_ITERATE(OIDCRedirectURLsAllowed,
+ oidc_set_redirect_urls_allowed,
+ (void *) APR_OFFSETOF(oidc_cfg,
redirect_urls_allowed),
+ RSRC_CONF|ACCESS_CONF|OR_AUTHCFG,
+ "Specify one or more regular expressions that
define URLs allowed for post logout and other redirects."),
+
{ NULL }
};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/jose.h
new/mod_auth_openidc-2.4.3/src/jose.h
--- old/mod_auth_openidc-2.4.2.1/src/jose.h 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/jose.h 2020-06-10 18:14:24.000000000
+0200
@@ -153,7 +153,7 @@
int kty;
/* key identifier */
char *kid;
- /* X.509 Certificate Chain */;
+ /* X.509 Certificate Chain */
unsigned char **x5c;
/* the size of the certificate chain */
int x5c_count;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/metadata.c
new/mod_auth_openidc-2.4.3/src/metadata.c
--- old/mod_auth_openidc-2.4.2.1/src/metadata.c 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/metadata.c 2020-06-10 18:14:24.000000000
+0200
@@ -109,6 +109,7 @@
#define OIDC_METADATA_POST_LOGOUT_REDIRECT_URIS
"post_logout_redirect_uris"
#define OIDC_METADATA_IDTOKEN_BINDING_CNF
"id_token_token_binding_cnf"
#define OIDC_METADATA_SSL_VALIDATE_SERVER
"ssl_validate_server"
+#define OIDC_METADATA_VALIDATE_ISSUER
"validate_issuer"
#define OIDC_METADATA_SCOPE "scope"
#define OIDC_METADATA_JWKS_REFRESH_INTERVAL
"jwks_refresh_interval"
#define OIDC_METADATA_IDTOKEN_IAT_SLACK
"idtoken_iat_slack"
@@ -1235,6 +1236,9 @@
oidc_metadata_parse_boolean(r, j_conf,
OIDC_METADATA_SSL_VALIDATE_SERVER,
&provider->ssl_validate_server,
cfg->provider.ssl_validate_server);
+ oidc_metadata_parse_boolean(r, j_conf, OIDC_METADATA_VALIDATE_ISSUER,
+ &provider->validate_issuer,
cfg->provider.validate_issuer);
+
/* find out what scopes we should be requesting from this provider */
// TODO: use the provider "scopes_supported" to mix-and-match with what
we've configured for the client
// TODO: check that "openid" is always included in the configured
scopes, right?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/mod_auth_openidc.c
new/mod_auth_openidc-2.4.3/src/mod_auth_openidc.c
--- old/mod_auth_openidc-2.4.2.1/src/mod_auth_openidc.c 2020-03-25
13:09:26.000000000 +0100
+++ new/mod_auth_openidc-2.4.3/src/mod_auth_openidc.c 2020-06-10
18:14:24.000000000 +0200
@@ -210,7 +210,7 @@
}
if (i == strip->nelts) {
- result = result ? apr_psprintf(r->pool,
"%s%s%s", result,
+ result = result ? apr_psprintf(r->pool, "%s%s
%s", result,
OIDC_STR_SEMI_COLON, cookie) :
cookie;
}
@@ -227,7 +227,8 @@
/*
* calculates a hash value based on request fingerprint plus a provided nonce
string.
*/
-static char *oidc_get_browser_state_hash(request_rec *r, const char *nonce) {
+static char *oidc_get_browser_state_hash(request_rec *r, oidc_cfg *c,
+ const char *nonce) {
oidc_debug(r, "enter");
@@ -239,17 +240,21 @@
/* Initialize the hash context */
apr_sha1_init(&sha1);
- /* get the X-FORWARDED-FOR header value */
- value = oidc_util_hdr_in_x_forwarded_for_get(r);
- /* if we have a value for this header, concat it to the hash input */
- if (value != NULL)
- apr_sha1_update(&sha1, value, strlen(value));
+ if (c->state_input_headers & OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR) {
+ /* get the X-FORWARDED-FOR header value */
+ value = oidc_util_hdr_in_x_forwarded_for_get(r);
+ /* if we have a value for this header, concat it to the hash
input */
+ if (value != NULL)
+ apr_sha1_update(&sha1, value, strlen(value));
+ }
- /* get the USER-AGENT header value */
- value = oidc_util_hdr_in_user_agent_get(r);
- /* if we have a value for this header, concat it to the hash input */
- if (value != NULL)
- apr_sha1_update(&sha1, value, strlen(value));
+ if (c->state_input_headers & OIDC_STATE_INPUT_HEADERS_USER_AGENT) {
+ /* get the USER-AGENT header value */
+ value = oidc_util_hdr_in_user_agent_get(r);
+ /* if we have a value for this header, concat it to the hash
input */
+ if (value != NULL)
+ apr_sha1_update(&sha1, value, strlen(value));
+ }
/* get the remote client IP address or host name */
/*
@@ -540,14 +545,13 @@
oidc_jose_error_t err;
oidc_jwk_t *jwk = NULL;
if (oidc_util_create_symmetric_key(r, c->provider.client_secret,
- oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256,
- TRUE, &jwk) == FALSE)
+ oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256, TRUE,
&jwk) == FALSE)
return FALSE;
oidc_jwt_t *jwt = NULL;
if (oidc_jwt_parse(r->pool, state, &jwt,
- oidc_util_merge_symmetric_key(r->pool, c->private_keys,
jwk),
- &err) == FALSE) {
+ oidc_util_merge_symmetric_key(r->pool, c->private_keys,
jwk), &err)
+ == FALSE) {
oidc_error(r,
"could not parse JWT from state: invalid
unsolicited response: %s",
oidc_jose_e2s(r->pool, err));
@@ -572,9 +576,10 @@
}
/* validate the state JWT, validating optional exp + iat */
- if (oidc_proto_validate_jwt(r, jwt, provider->issuer, FALSE, FALSE,
- provider->idtoken_iat_slack,
- OIDC_TOKEN_BINDING_POLICY_DISABLED) == FALSE) {
+ if (oidc_proto_validate_jwt(r, jwt,
+ provider->validate_issuer ? provider->issuer : NULL,
FALSE, FALSE,
+ provider->idtoken_iat_slack,
+ OIDC_TOKEN_BINDING_POLICY_DISABLED) ==
FALSE) {
oidc_jwt_destroy(jwt);
return FALSE;
}
@@ -598,8 +603,7 @@
char *target_link_uri = NULL;
oidc_jose_get_string(r->pool, jwt->payload.value.json,
- OIDC_CLAIM_TARGET_LINK_URI,
- FALSE, &target_link_uri, NULL);
+ OIDC_CLAIM_TARGET_LINK_URI, FALSE, &target_link_uri,
NULL);
if (target_link_uri == NULL) {
if (c->default_sso_url == NULL) {
oidc_error(r,
@@ -826,7 +830,7 @@
const char *nonce = oidc_proto_state_get_nonce(*proto_state);
/* calculate the hash of the browser fingerprint concatenated with the
nonce */
- char *calc = oidc_get_browser_state_hash(r, nonce);
+ char *calc = oidc_get_browser_state_hash(r, c, nonce);
/* compare the calculated hash with the value provided in the
authorization response */
if (apr_strnatcmp(calc, state) != 0) {
oidc_error(r,
@@ -1226,8 +1230,8 @@
/* refresh the tokens by calling the token endpoint */
if (oidc_proto_refresh_request(r, c, provider, refresh_token,
&s_id_token,
- &s_access_token, &s_token_type, &expires_in,
- &s_refresh_token) == FALSE) {
+ &s_access_token, &s_token_type, &expires_in,
&s_refresh_token)
+ == FALSE) {
oidc_error(r, "access_token could not be refreshed");
return FALSE;
}
@@ -1306,7 +1310,8 @@
/* try again with the new access token */
if (oidc_proto_resolve_userinfo(r, c, provider,
id_token_sub,
- refreshed_access_token,
&result, userinfo_jwt) == FALSE) {
+ refreshed_access_token,
&result, userinfo_jwt)
+ == FALSE) {
oidc_error(r,
"resolving user info
claims with the refreshed access token failed, nothing will be stored in the
session");
@@ -1481,7 +1486,8 @@
}
static apr_byte_t oidc_refresh_access_token_before_expiry(request_rec *r,
- oidc_cfg *cfg, oidc_session_t *session, int ttl_minimum, int
logout_on_error) {
+ oidc_cfg *cfg, oidc_session_t *session, int ttl_minimum,
+ int logout_on_error) {
const char *s_access_token_expires = NULL;
apr_time_t t_expires = -1;
@@ -1524,7 +1530,8 @@
if (oidc_refresh_access_token(r, cfg, session, provider,
NULL) == FALSE) {
- oidc_warn(r, "access_token could not be refreshed, logout=%d",
logout_on_error & OIDC_LOGOUT_ON_ERROR_REFRESH);
+ oidc_warn(r, "access_token could not be refreshed, logout=%d",
+ logout_on_error & OIDC_LOGOUT_ON_ERROR_REFRESH);
if (logout_on_error & OIDC_LOGOUT_ON_ERROR_REFRESH)
return ERROR;
else
@@ -2369,8 +2376,8 @@
char *javascript = NULL, *javascript_method = NULL;
char *html_head =
"<style type=\"text/css\">body {text-align:
center}</style>";
- if (oidc_post_preserve_javascript(r, NULL, &javascript,
- &javascript_method) == TRUE)
+ if (oidc_post_preserve_javascript(r, NULL, &javascript,
&javascript_method)
+ == TRUE)
html_head = apr_psprintf(r->pool, "%s%s", html_head,
javascript);
/* now send the HTML contents to the user agent */
@@ -2437,7 +2444,7 @@
oidc_proto_state_set_pkce_state(proto_state, pkce_state);
/* get a hash value that fingerprints the browser concatenated with the
random input */
- char *state = oidc_get_browser_state_hash(r, nonce);
+ char *state = oidc_get_browser_state_hash(r, c, nonce);
/*
* create state that restores the context when the authorization
response comes in
@@ -2613,8 +2620,8 @@
}
/* do open redirect prevention */
- if (oidc_target_link_uri_matches_configuration(r, c,
- target_link_uri) == FALSE) {
+ if (oidc_target_link_uri_matches_configuration(r, c, target_link_uri)
+ == FALSE) {
return oidc_util_html_send_error(r, c->error_template,
"Invalid Request",
"\"target_link_uri\" parameter does not match
configuration settings, aborting to prevent an open redirect.",
@@ -2669,7 +2676,8 @@
}
/* got an account name as input, perform OP discovery with that
*/
- if (oidc_proto_account_based_discovery(r, c, issuer, &issuer)
== FALSE) {
+ if (oidc_proto_account_based_discovery(r, c, issuer, &issuer)
+ == FALSE) {
/* something did not work out, show a user facing error
*/
return oidc_util_html_send_error(r, c->error_template,
@@ -2912,15 +2920,15 @@
// oidc_proto_validate_idtoken would try and require a token binding cnf
// if the policy is set to "required", so don't use that here
-
- if (oidc_proto_validate_jwt(r, jwt, provider->issuer, FALSE, FALSE,
- provider->idtoken_iat_slack,
- OIDC_TOKEN_BINDING_POLICY_DISABLED) == FALSE)
+ if (oidc_proto_validate_jwt(r, jwt,
+ provider->validate_issuer ? provider->issuer : NULL,
FALSE, FALSE,
+ provider->idtoken_iat_slack,
+ OIDC_TOKEN_BINDING_POLICY_DISABLED) ==
FALSE)
goto out;
/* verify the "aud" and "azp" values */
- if (oidc_proto_validate_aud_and_azp(r, cfg, provider,
- &jwt->payload) == FALSE)
+ if (oidc_proto_validate_aud_and_azp(r, cfg, provider, &jwt->payload)
+ == FALSE)
goto out;
json_t *events = json_object_get(jwt->payload.value.json,
@@ -3037,30 +3045,51 @@
return rc;
}
-static apr_byte_t oidc_validate_post_logout_url(request_rec *r, const char
*url,
- char **err_str, char **err_desc) {
+static apr_byte_t oidc_validate_redirect_url(request_rec *r, oidc_cfg *c,
+ const char *url, char **err_str, char **err_desc) {
apr_uri_t uri;
const char *c_host = NULL;
+ apr_hash_index_t *hi = NULL;
if (apr_uri_parse(r->pool, url, &uri) != APR_SUCCESS) {
*err_str = apr_pstrdup(r->pool, "Malformed URL");
- *err_desc = apr_psprintf(r->pool, "Logout URL malformed: %s",
url);
+ *err_desc = apr_psprintf(r->pool, "not a valid URL value: %s",
url);
oidc_error(r, "%s: %s", *err_str, *err_desc);
return FALSE;
}
- c_host = oidc_get_current_url_host(r);
- if ((uri.hostname != NULL)
- && ((strstr(c_host, uri.hostname) == NULL)
- || (strstr(uri.hostname, c_host) ==
NULL))) {
- *err_str = apr_pstrdup(r->pool, "Invalid Request");
- *err_desc =
- apr_psprintf(r->pool,
- "logout value \"%s\" does not
match the hostname of the current request \"%s\"",
- apr_uri_unparse(r->pool, &uri,
0), c_host);
- oidc_error(r, "%s: %s", *err_str, *err_desc);
- return FALSE;
- } else if ((uri.hostname == NULL) && (strstr(url, "/") != url)) {
+ if (c->redirect_urls_allowed != NULL) {
+ for (hi = apr_hash_first(NULL, c->redirect_urls_allowed); hi;
hi =
+ apr_hash_next(hi)) {
+ apr_hash_this(hi, (const void**) &c_host, NULL, NULL);
+ if (oidc_util_regexp_first_match(r->pool, url, c_host,
+ NULL, err_str) == TRUE)
+ break;
+ }
+ if (hi == NULL) {
+ *err_str = apr_pstrdup(r->pool, "URL not allowed");
+ *err_desc =
+ apr_psprintf(r->pool,
+ "value does not match
the list of allowed redirect URLs: %s",
+ url);
+ oidc_error(r, "%s: %s", *err_str, *err_desc);
+ return FALSE;
+ }
+ } else if (uri.hostname != NULL) {
+ c_host = oidc_get_current_url_host(r);
+ if ((strstr(c_host, uri.hostname) == NULL)
+ || (strstr(uri.hostname, c_host) == NULL)) {
+ *err_str = apr_pstrdup(r->pool, "Invalid Request");
+ *err_desc =
+ apr_psprintf(r->pool,
+ "URL value \"%s\" does
not match the hostname of the current request \"%s\"",
+
apr_uri_unparse(r->pool, &uri, 0), c_host);
+ oidc_error(r, "%s: %s", *err_str, *err_desc);
+ return FALSE;
+ }
+ }
+
+ if ((uri.hostname == NULL) && (strstr(url, "/") != url)) {
*err_str = apr_pstrdup(r->pool, "Malformed URL");
*err_desc =
apr_psprintf(r->pool,
@@ -3068,30 +3097,26 @@
url);
oidc_error(r, "%s: %s", *err_str, *err_desc);
return FALSE;
- } else if ((uri.hostname == NULL) && (strstr(url, "//") == url)) {
- *err_str = apr_pstrdup(r->pool, "Malformed URL");
- *err_desc =
- apr_psprintf(r->pool,
- "No hostname was parsed and
starting with '//': %s",
- url);
- oidc_error(r, "%s: %s", *err_str, *err_desc);
- return FALSE;
- } else if ((uri.hostname == NULL) && (strstr(url, "/\\") == url)) {
- *err_str = apr_pstrdup(r->pool, "Malformed URL");
- *err_desc =
- apr_psprintf(r->pool,
- "No hostname was parsed and
starting with '/\\': %s",
- url);
- oidc_error(r, "%s: %s", *err_str, *err_desc);
- return FALSE;
+ } else if ((uri.hostname == NULL) && (strstr(url, "//") == url)) {
+ *err_str = apr_pstrdup(r->pool, "Malformed URL");
+ *err_desc = apr_psprintf(r->pool,
+ "No hostname was parsed and starting with '//':
%s", url);
+ oidc_error(r, "%s: %s", *err_str, *err_desc);
+ return FALSE;
+ } else if ((uri.hostname == NULL) && (strstr(url, "/\\") == url)) {
+ *err_str = apr_pstrdup(r->pool, "Malformed URL");
+ *err_desc = apr_psprintf(r->pool,
+ "No hostname was parsed and starting with
'/\\': %s", url);
+ oidc_error(r, "%s: %s", *err_str, *err_desc);
+ return FALSE;
}
/* validate the URL to prevent HTTP header splitting */
if (((strstr(url, "\n") != NULL) || strstr(url, "\r") != NULL)) {
- *err_str = apr_pstrdup(r->pool, "Invalid Request");
+ *err_str = apr_pstrdup(r->pool, "Invalid URL");
*err_desc =
apr_psprintf(r->pool,
- "logout value \"%s\" contains
illegal \"\n\" or \"\r\" character(s)",
+ "URL value \"%s\" contains
illegal \"\n\" or \"\r\" character(s)",
url);
oidc_error(r, "%s: %s", *err_str, *err_desc);
return FALSE;
@@ -3129,7 +3154,7 @@
} else {
/* do input validation on the logout parameter value */
- if (oidc_validate_post_logout_url(r, url, &error_str,
+ if (oidc_validate_redirect_url(r, c, url, &error_str,
&error_description) == FALSE) {
return oidc_util_html_send_error(r, c->error_template,
error_str,
error_description,
@@ -3388,6 +3413,8 @@
char *return_to = NULL;
char *r_access_token = NULL;
char *error_code = NULL;
+ char *error_str = NULL;
+ char *error_description = NULL;
/* get the command passed to the session management handler */
oidc_util_get_request_parameter(r, OIDC_REDIRECT_URI_REQUEST_REFRESH,
@@ -3402,6 +3429,14 @@
return HTTP_INTERNAL_SERVER_ERROR;
}
+ /* do input validation on the return to parameter value */
+ if (oidc_validate_redirect_url(r, c, return_to, &error_str,
+ &error_description) == FALSE) {
+ oidc_error(r, "return_to URL validation failed: %s: %s",
error_str,
+ error_description);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
if (r_access_token == NULL) {
oidc_error(r,
"refresh token request handler called with no
access_token parameter");
@@ -3564,8 +3599,8 @@
/* get the current provider info */
oidc_provider_t *provider = NULL;
- if (oidc_get_provider_from_session(r, c,
session,
- &provider) == FALSE)
+ if (oidc_get_provider_from_session(r, c,
session, &provider)
+ == FALSE)
return HTTP_INTERNAL_SERVER_ERROR;
/* execute the actual refresh grant */
@@ -3693,15 +3728,15 @@
/* this is an authorization response from the OP using the
Basic Client profile or a Hybrid flow*/
return oidc_handle_redirect_authorization_response(r, c,
session);
- /*
- *
- * Note that we are checking for logout *before* checking for a POST
authorization response
- * to handle backchannel POST-based logout
- *
- * so any POST to the Redirect URI that does not have a logout query
parameter will be handled
- * as an authorization response; alternatively we could assume that a
POST response has no
- * parameters
- */
+ /*
+ *
+ * Note that we are checking for logout *before* checking for a
POST authorization response
+ * to handle backchannel POST-based logout
+ *
+ * so any POST to the Redirect URI that does not have a logout
query parameter will be handled
+ * as an authorization response; alternatively we could assume
that a POST response has no
+ * parameters
+ */
} else if (oidc_util_request_has_parameter(r,
OIDC_REDIRECT_URI_REQUEST_LOGOUT)) {
/* handle logout */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/mod_auth_openidc.h
new/mod_auth_openidc-2.4.3/src/mod_auth_openidc.h
--- old/mod_auth_openidc-2.4.2.1/src/mod_auth_openidc.h 2020-03-25
13:09:26.000000000 +0100
+++ new/mod_auth_openidc-2.4.3/src/mod_auth_openidc.h 2020-06-10
18:14:24.000000000 +0200
@@ -231,6 +231,9 @@
#define OIDC_TOKEN_BINDING_POLICY_REQUIRED 2
#define OIDC_TOKEN_BINDING_POLICY_ENFORCED 3
+#define OIDC_STATE_INPUT_HEADERS_USER_AGENT 1
+#define OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR 2
+
typedef apr_byte_t (*oidc_proto_pkce_state)(request_rec *r, char **state);
typedef apr_byte_t (*oidc_proto_pkce_challenge)(request_rec *r, const char
*state, char **code_challenge);
typedef apr_byte_t (*oidc_proto_pkce_verifier)(request_rec *r, const char
*state, char **code_verifier);
@@ -273,6 +276,7 @@
// the next ones function as global default settings too
int ssl_validate_server;
+ int validate_issuer;
char *client_name;
char *client_contact;
char *registration_token;
@@ -417,6 +421,9 @@
apr_hash_t *black_listed_claims;
apr_hash_t *white_listed_claims;
+ apr_byte_t state_input_headers;
+
+ apr_hash_t *redirect_urls_allowed;
} oidc_cfg;
int oidc_check_user_id(request_rec *r);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/parse.c
new/mod_auth_openidc-2.4.3/src/parse.c
--- old/mod_auth_openidc-2.4.2.1/src/parse.c 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/parse.c 2020-06-10 18:14:24.000000000
+0200
@@ -1324,3 +1324,36 @@
return oidc_parse_int_valid(pool, arg, int_value,
oidc_valid_refresh_access_token_before_expiry);
}
+
+#define OIDC_STATE_INPUT_HEADERS_AS_BOTH "both"
+#define OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT "user-agent"
+#define OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR "x-forwarded-for"
+#define OIDC_STATE_INPUT_HEADERS_AS_NONE "none"
+
+/*
+ * parse a "set state input headers as" value from the provided string
+ */
+const char *oidc_parse_set_state_input_headers_as(apr_pool_t *pool, const char
*arg,
+ apr_byte_t *state_input_headers) {
+ static char *options[] = {
+ OIDC_STATE_INPUT_HEADERS_AS_BOTH,
+ OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT,
+ OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR,
+ OIDC_STATE_INPUT_HEADERS_AS_NONE,
+ NULL };
+ const char *rv = oidc_valid_string_option(pool, arg, options);
+ if (rv != NULL)
+ return rv;
+
+ if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_BOTH) == 0) {
+ *state_input_headers = OIDC_STATE_INPUT_HEADERS_USER_AGENT |
OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR;
+ } else if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT)
== 0) {
+ *state_input_headers = OIDC_STATE_INPUT_HEADERS_USER_AGENT;
+ } else if (apr_strnatcmp(arg,
OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR) == 0) {
+ *state_input_headers = OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR;
+ } else if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_NONE) == 0) {
+ *state_input_headers = 0;
+ }
+
+ return NULL;
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/parse.h
new/mod_auth_openidc-2.4.3/src/parse.h
--- old/mod_auth_openidc-2.4.2.1/src/parse.h 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/parse.h 2020-06-10 18:14:24.000000000
+0200
@@ -122,6 +122,7 @@
const char *oidc_parse_auth_request_method(apr_pool_t *pool, const char *arg,
int *method);
const char *oidc_parse_max_number_of_state_cookies(apr_pool_t *pool, const
char *arg1, const char *arg2, int *int_value, int *bool_value);
const char *oidc_parse_refresh_access_token_before_expiry(apr_pool_t *pool,
const char *arg, int *int_value);
+const char *oidc_parse_set_state_input_headers_as(apr_pool_t *pool, const char
*arg, apr_byte_t *state_input_headers);
typedef const char *(*oidc_valid_int_function_t)(apr_pool_t *, int);
typedef const char *(*oidc_valid_function_t)(apr_pool_t *, const char *);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/proto.c
new/mod_auth_openidc-2.4.3/src/proto.c
--- old/mod_auth_openidc-2.4.2.1/src/proto.c 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/proto.c 2020-06-10 18:14:24.000000000
+0200
@@ -1354,7 +1354,7 @@
}
/* validate the ID Token JWT, requiring iss match, and valid exp + iat
*/
- if (oidc_proto_validate_jwt(r, jwt, provider->issuer, TRUE, TRUE,
+ if (oidc_proto_validate_jwt(r, jwt, provider->validate_issuer ?
provider->issuer : NULL, TRUE, TRUE,
provider->idtoken_iat_slack,
provider->token_binding_policy) == FALSE)
return FALSE;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/mod_auth_openidc-2.4.2.1/src/util.c
new/mod_auth_openidc-2.4.3/src/util.c
--- old/mod_auth_openidc-2.4.2.1/src/util.c 2020-03-25 13:09:26.000000000
+0100
+++ new/mod_auth_openidc-2.4.3/src/util.c 2020-06-10 18:14:24.000000000
+0200
@@ -2267,14 +2267,18 @@
goto out;
}
- if (pcre_get_substring(input, subStr, rc, OIDC_UTIL_REGEXP_MATCH_NR,
- &(psubStrMatchStr)) <= 0) {
- *error_str = apr_psprintf(pool, "pcre_get_substring failed
(rc=%d)",
- rc);
- goto out;
+ if (output) {
+
+ if (pcre_get_substring(input, subStr, rc,
OIDC_UTIL_REGEXP_MATCH_NR,
+ &(psubStrMatchStr)) <= 0) {
+ *error_str = apr_psprintf(pool, "pcre_get_substring
failed (rc=%d)",
+ rc);
+ goto out;
+ }
+
+ *output = apr_pstrdup(pool, psubStrMatchStr);
}
- *output = apr_pstrdup(pool, psubStrMatchStr);
rv = TRUE;
out: