Blame SOURCES/0012-optionally-delete-the-oldest-state-cookie-s-see-399.patch

5b8408
From 95baad3342aa7ef7172ad4922eb9f0d605dc469b Mon Sep 17 00:00:00 2001
5b8408
From: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
5b8408
Date: Thu, 6 Dec 2018 14:55:44 +0100
5b8408
Subject: [PATCH 12/13] optionally delete the oldest state cookie(s); see #399
5b8408
5b8408
bump to 2.3.10rc3
5b8408
5b8408
Signed-off-by: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
5b8408
(cherry picked from commit 46758a75eef8e3c91f2917fc7c6302136eb18809)
5b8408
---
5b8408
 auth_openidc.conf      |  8 +++--
5b8408
 src/config.c           | 27 +++++++++++++----
5b8408
 src/mod_auth_openidc.c | 66 +++++++++++++++++++++++++++++++++++++++---
5b8408
 src/mod_auth_openidc.h |  2 ++
5b8408
 src/parse.c            |  9 ++++--
5b8408
 src/parse.h            |  2 +-
5b8408
 6 files changed, 100 insertions(+), 14 deletions(-)
5b8408
5b8408
diff --git a/auth_openidc.conf b/auth_openidc.conf
5b8408
index 48dd027..33cea64 100644
5b8408
--- a/auth_openidc.conf
5b8408
+++ b/auth_openidc.conf
5b8408
@@ -450,8 +450,12 @@
5b8408
 # authentication requests. See: https://github.com/zmartzone/mod_auth_openidc/issues/331
5b8408
 # Setting this to 0 means unlimited, until the browser or server gives up which is the
5b8408
 # behavior of mod_auth_openidc < 2.3.8, which did not have this configuration option. 
5b8408
-# When not defined, the default is 7.
5b8408
-#OIDCStateMaxNumberOfCookies <number>
5b8408
+#
5b8408
+# The optional second boolean parameter if the oldest state cookie(s) will be deleted, 
5b8408
+# even if still valid; see #399.
5b8408
+#
5b8408
+# When not defined, the default is 7 and "false", thus the oldest cookie(s) will not be deleted.
5b8408
+#OIDCStateMaxNumberOfCookies <number> [false|true]
5b8408
 
5b8408
 ########################################################################################
5b8408
 #
5b8408
diff --git a/src/config.c b/src/config.c
5b8408
index 6fa6227..8e56716 100644
5b8408
--- a/src/config.c
5b8408
+++ b/src/config.c
5b8408
@@ -106,6 +106,8 @@
5b8408
 #define OIDC_DEFAULT_STATE_TIMEOUT 300
5b8408
 /* maximum number of parallel state cookies; 0 means unlimited, until the browser or server gives up */
5b8408
 #define OIDC_DEFAULT_MAX_NUMBER_OF_STATE_COOKIES 7
5b8408
+/* default setting for deleting the oldest state cookies */
5b8408
+#define OIDC_DEFAULT_DELETE_OLDEST_STATE_COOKIES 0
5b8408
 /* default session inactivity timeout */
5b8408
 #define OIDC_DEFAULT_SESSION_INACTIVITY_TIMEOUT 300
5b8408
 /* default session max duration */
5b8408
@@ -1001,11 +1003,12 @@ static const char *oidc_set_client_auth_bearer_token(cmd_parms *cmd,
5b8408
  * set the maximun number of parallel state cookies
5b8408
  */
5b8408
 static const char *oidc_set_max_number_of_state_cookies(cmd_parms *cmd,
5b8408
-		void *struct_ptr, const char *arg) {
5b8408
+		void *struct_ptr, const char *arg1, const char *arg2) {
5b8408
 	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
5b8408
 			cmd->server->module_config, &auth_openidc_module);
5b8408
-	const char *rv = oidc_parse_max_number_of_state_cookies(cmd->pool, arg,
5b8408
-			&cfg->max_number_of_state_cookies);
5b8408
+	const char *rv = oidc_parse_max_number_of_state_cookies(cmd->pool, arg1,
5b8408
+			arg2, &cfg->max_number_of_state_cookies,
5b8408
+			&cfg->delete_oldest_state_cookies);
5b8408
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
5b8408
 }
5b8408
 
5b8408
@@ -1018,6 +1021,15 @@ int oidc_cfg_max_number_of_state_cookies(oidc_cfg *cfg) {
5b8408
 	return cfg->max_number_of_state_cookies;
5b8408
 }
5b8408
 
5b8408
+/*
5b8408
+ * return the number of oldest state cookies that need to be deleted
5b8408
+ */
5b8408
+int oidc_cfg_delete_oldest_state_cookies(oidc_cfg *cfg) {
5b8408
+	if (cfg->delete_oldest_state_cookies == OIDC_CONFIG_POS_INT_UNSET)
5b8408
+		return OIDC_DEFAULT_DELETE_OLDEST_STATE_COOKIES;
5b8408
+	return cfg->delete_oldest_state_cookies;
5b8408
+}
5b8408
+
5b8408
 /*
5b8408
  * create a new server config record with defaults
5b8408
  */
5b8408
@@ -1127,6 +1139,7 @@ void *oidc_create_server_config(apr_pool_t *pool, server_rec *svr) {
5b8408
 	c->http_timeout_short = OIDC_DEFAULT_HTTP_TIMEOUT_SHORT;
5b8408
 	c->state_timeout = OIDC_DEFAULT_STATE_TIMEOUT;
5b8408
 	c->max_number_of_state_cookies = OIDC_CONFIG_POS_INT_UNSET;
5b8408
+	c->delete_oldest_state_cookies = OIDC_CONFIG_POS_INT_UNSET;
5b8408
 	c->session_inactivity_timeout = OIDC_DEFAULT_SESSION_INACTIVITY_TIMEOUT;
5b8408
 
5b8408
 	c->cookie_domain = NULL;
5b8408
@@ -1445,6 +1458,10 @@ void *oidc_merge_server_config(apr_pool_t *pool, void *BASE, void *ADD) {
5b8408
 			add->max_number_of_state_cookies != OIDC_CONFIG_POS_INT_UNSET ?
5b8408
 					add->max_number_of_state_cookies :
5b8408
 					base->max_number_of_state_cookies;
5b8408
+	c->delete_oldest_state_cookies =
5b8408
+			add->delete_oldest_state_cookies != OIDC_CONFIG_POS_INT_UNSET ?
5b8408
+					add->delete_oldest_state_cookies :
5b8408
+					base->delete_oldest_state_cookies;
5b8408
 	c->session_inactivity_timeout =
5b8408
 			add->session_inactivity_timeout
5b8408
 			!= OIDC_DEFAULT_SESSION_INACTIVITY_TIMEOUT ?
5b8408
@@ -2656,11 +2673,11 @@ const command_rec oidc_config_cmds[] = {
5b8408
 				(void*)APR_OFFSETOF(oidc_cfg, state_timeout),
5b8408
 				RSRC_CONF,
5b8408
 				"Time to live in seconds for state parameter (cq. interval in which the authorization request and the corresponding response need to be completed)."),
5b8408
-		AP_INIT_TAKE1(OIDCStateMaxNumberOfCookies,
5b8408
+		AP_INIT_TAKE12(OIDCStateMaxNumberOfCookies,
5b8408
 				oidc_set_max_number_of_state_cookies,
5b8408
 				(void*)APR_OFFSETOF(oidc_cfg, max_number_of_state_cookies),
5b8408
 				RSRC_CONF,
5b8408
-				"Maximun number of parallel state cookies i.e. outstanding authorization requests."),
5b8408
+				"Maximun number of parallel state cookies i.e. outstanding authorization requests and whether to delete the oldest cookie(s)."),
5b8408
 		AP_INIT_TAKE1(OIDCSessionInactivityTimeout,
5b8408
 				oidc_set_session_inactivity_timeout,
5b8408
 				(void*)APR_OFFSETOF(oidc_cfg, session_inactivity_timeout),
5b8408
diff --git a/src/mod_auth_openidc.c b/src/mod_auth_openidc.c
5b8408
index 897a449..8740e02 100644
5b8408
--- a/src/mod_auth_openidc.c
5b8408
+++ b/src/mod_auth_openidc.c
5b8408
@@ -686,15 +686,53 @@ static apr_byte_t oidc_unsolicited_proto_state(request_rec *r, oidc_cfg *c,
5b8408
 	return TRUE;
5b8408
 }
5b8408
 
5b8408
+typedef struct oidc_state_cookies_t {
5b8408
+	char *name;
5b8408
+	apr_time_t timestamp;
5b8408
+	struct oidc_state_cookies_t *next;
5b8408
+} oidc_state_cookies_t;
5b8408
+
5b8408
+static int oidc_delete_oldest_state_cookies(request_rec *r,
5b8408
+		int number_of_valid_state_cookies, int max_number_of_state_cookies,
5b8408
+		oidc_state_cookies_t *first) {
5b8408
+	oidc_state_cookies_t *cur = NULL, *prev = NULL, *prev_oldest = NULL,
5b8408
+			*oldest = NULL;
5b8408
+	while (number_of_valid_state_cookies >= max_number_of_state_cookies) {
5b8408
+		oldest = first;
5b8408
+		prev_oldest = NULL;
5b8408
+		prev = first;
5b8408
+		cur = first->next;
5b8408
+		while (cur) {
5b8408
+			if ((cur->timestamp < oldest->timestamp)) {
5b8408
+				oldest = cur;
5b8408
+				prev_oldest = prev;
5b8408
+			}
5b8408
+			prev = cur;
5b8408
+			cur = cur->next;
5b8408
+		}
5b8408
+		oidc_warn(r,
5b8408
+				"deleting oldest state cookie: %s (time until expiry " APR_TIME_T_FMT " seconds)",
5b8408
+				oldest->name, apr_time_sec(oldest->timestamp - apr_time_now()));
5b8408
+		oidc_util_set_cookie(r, oldest->name, "", 0, NULL);
5b8408
+		if (prev_oldest)
5b8408
+			prev_oldest->next = oldest->next;
5b8408
+		else
5b8408
+			first = first->next;
5b8408
+		number_of_valid_state_cookies--;
5b8408
+	}
5b8408
+	return number_of_valid_state_cookies;
5b8408
+}
5b8408
+
5b8408
 /*
5b8408
  * clean state cookies that have expired i.e. for outstanding requests that will never return
5b8408
  * successfully and return the number of remaining valid cookies/outstanding-requests while
5b8408
  * doing so
5b8408
  */
5b8408
 static int oidc_clean_expired_state_cookies(request_rec *r, oidc_cfg *c,
5b8408
-		const char *currentCookieName) {
5b8408
+		const char *currentCookieName, int delete_oldest) {
5b8408
 	int number_of_valid_state_cookies = 0;
5b8408
-	char *cookie, *tokenizerCtx;
5b8408
+	oidc_state_cookies_t *first = NULL, *last = NULL;
5b8408
+	char *cookie, *tokenizerCtx = NULL;
5b8408
 	char *cookies = apr_pstrdup(r->pool, oidc_util_hdr_in_cookie_get(r));
5b8408
 	if (cookies != NULL) {
5b8408
 		cookie = apr_strtok(cookies, OIDC_STR_SEMI_COLON, &tokenizerCtx);
5b8408
@@ -722,6 +760,18 @@ static int oidc_clean_expired_state_cookies(request_rec *r, oidc_cfg *c,
5b8408
 								oidc_util_set_cookie(r, cookieName, "", 0,
5b8408
 										NULL);
5b8408
 							} else {
5b8408
+								if (first == NULL) {
5b8408
+									first = apr_pcalloc(r->pool,
5b8408
+											sizeof(oidc_state_cookies_t));
5b8408
+									last = first;
5b8408
+								} else {
5b8408
+									last->next = apr_pcalloc(r->pool,
5b8408
+											sizeof(oidc_state_cookies_t));
5b8408
+									last = last->next;
5b8408
+								}
5b8408
+								last->name = cookieName;
5b8408
+								last->timestamp = ts;
5b8408
+								last->next = NULL;
5b8408
 								number_of_valid_state_cookies++;
5b8408
 							}
5b8408
 							oidc_proto_state_destroy(proto_state);
5b8408
@@ -732,6 +782,12 @@ static int oidc_clean_expired_state_cookies(request_rec *r, oidc_cfg *c,
5b8408
 			cookie = apr_strtok(NULL, OIDC_STR_SEMI_COLON, &tokenizerCtx);
5b8408
 		}
5b8408
 	}
5b8408
+
5b8408
+	if (delete_oldest > 0)
5b8408
+		number_of_valid_state_cookies = oidc_delete_oldest_state_cookies(r,
5b8408
+				number_of_valid_state_cookies, c->max_number_of_state_cookies,
5b8408
+				first);
5b8408
+
5b8408
 	return number_of_valid_state_cookies;
5b8408
 }
5b8408
 
5b8408
@@ -746,7 +802,7 @@ static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c,
5b8408
 	const char *cookieName = oidc_get_state_cookie_name(r, state);
5b8408
 
5b8408
 	/* clean expired state cookies to avoid pollution */
5b8408
-	oidc_clean_expired_state_cookies(r, c, cookieName);
5b8408
+	oidc_clean_expired_state_cookies(r, c, cookieName, FALSE);
5b8408
 
5b8408
 	/* get the state cookie value first */
5b8408
 	char *cookieValue = oidc_util_get_cookie(r, cookieName);
5b8408
@@ -820,10 +876,12 @@ static int oidc_authorization_request_set_cookie(request_rec *r, oidc_cfg *c,
5b8408
 	 * clean expired state cookies to avoid pollution and optionally
5b8408
 	 * try to avoid the number of state cookies exceeding a max
5b8408
 	 */
5b8408
-	int number_of_cookies = oidc_clean_expired_state_cookies(r, c, NULL);
5b8408
+	int number_of_cookies = oidc_clean_expired_state_cookies(r, c, NULL,
5b8408
+			oidc_cfg_delete_oldest_state_cookies(c));
5b8408
 	int max_number_of_cookies = oidc_cfg_max_number_of_state_cookies(c);
5b8408
 	if ((max_number_of_cookies > 0)
5b8408
 			&& (number_of_cookies >= max_number_of_cookies)) {
5b8408
+
5b8408
 		oidc_warn(r,
5b8408
 				"the number of existing, valid state cookies (%d) has exceeded the limit (%d), no additional authorization request + state cookie can be generated, aborting the request",
5b8408
 				number_of_cookies, max_number_of_cookies);
5b8408
diff --git a/src/mod_auth_openidc.h b/src/mod_auth_openidc.h
5b8408
index f89f392..c3a0a23 100644
5b8408
--- a/src/mod_auth_openidc.h
5b8408
+++ b/src/mod_auth_openidc.h
5b8408
@@ -380,6 +380,7 @@ typedef struct oidc_cfg {
5b8408
 	int http_timeout_short;
5b8408
 	int state_timeout;
5b8408
 	int max_number_of_state_cookies;
5b8408
+	int delete_oldest_state_cookies;
5b8408
 	int session_inactivity_timeout;
5b8408
 	int session_cache_fallback_to_cookie;
5b8408
 
5b8408
@@ -691,6 +692,7 @@ int oidc_cfg_session_cache_fallback_to_cookie(request_rec *r);
5b8408
 const char *oidc_parse_pkce_type(apr_pool_t *pool, const char *arg, oidc_proto_pkce_t **type);
5b8408
 const char *oidc_cfg_claim_prefix(request_rec *r);
5b8408
 int oidc_cfg_max_number_of_state_cookies(oidc_cfg *cfg);
5b8408
+int oidc_cfg_delete_oldest_state_cookies(oidc_cfg *cfg);
5b8408
 
5b8408
 // oidc_util.c
5b8408
 int oidc_strnenvcmp(const char *a, const char *b, int len);
5b8408
diff --git a/src/parse.c b/src/parse.c
5b8408
index 0f986fd..2d09584 100644
5b8408
--- a/src/parse.c
5b8408
+++ b/src/parse.c
5b8408
@@ -1245,7 +1245,12 @@ const char *oidc_parse_auth_request_method(apr_pool_t *pool, const char *arg,
5b8408
  * parse the maximum number of parallel state cookies
5b8408
  */
5b8408
 const char *oidc_parse_max_number_of_state_cookies(apr_pool_t *pool,
5b8408
-		const char *arg, int *int_value) {
5b8408
-	return oidc_parse_int_valid(pool, arg, int_value,
5b8408
+		const char *arg1, const char *arg2, int *int_value, int *bool_value) {
5b8408
+	const char *rv = NULL;
5b8408
+
5b8408
+	rv = oidc_parse_int_valid(pool, arg1, int_value,
5b8408
 			oidc_valid_max_number_of_state_cookies);
5b8408
+	if ((rv == NULL) && (arg2 != NULL))
5b8408
+		rv = oidc_parse_boolean(pool, arg2, bool_value);
5b8408
+	return rv;
5b8408
 }
5b8408
diff --git a/src/parse.h b/src/parse.h
5b8408
index 6355db4..bdf5651 100644
5b8408
--- a/src/parse.h
5b8408
+++ b/src/parse.h
5b8408
@@ -117,7 +117,7 @@ const char *oidc_parse_info_hook_data(apr_pool_t *pool, const char *arg, apr_has
5b8408
 const char *oidc_parse_token_binding_policy(apr_pool_t *pool, const char *arg, int *int_value);
5b8408
 const char *oidc_token_binding_policy2str(apr_pool_t *pool, int v);
5b8408
 const char *oidc_parse_auth_request_method(apr_pool_t *pool, const char *arg, int *method);
5b8408
-const char *oidc_parse_max_number_of_state_cookies(apr_pool_t *pool, const char *arg, int *int_value);
5b8408
+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);
5b8408
 
5b8408
 typedef const char *(*oidc_valid_int_function_t)(apr_pool_t *, int);
5b8408
 typedef const char *(*oidc_valid_function_t)(apr_pool_t *, const char *);
5b8408
-- 
5b8408
2.26.2
5b8408