Blob Blame History Raw
From 9a0827a18ec4d16cf9e79afede901194d6ef5cc6 Mon Sep 17 00:00:00 2001
From: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
Date: Thu, 23 Aug 2018 00:04:38 +0200
Subject: [PATCH 10/11] improve auto-detection of XMLHttpRequests via Accept
 header; see #331

version 2.3.8rc5

Signed-off-by: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
(cherry picked from commit cff35bba8861ddb09d694ecfcd88d5fac1018453)
---
 src/mod_auth_openidc.c |  41 +++---
 src/mod_auth_openidc.h |   6 +-
 src/util.c             |  80 +++++++---

diff --git a/src/mod_auth_openidc.c b/src/mod_auth_openidc.c
index e3817a9..897a449 100644
--- a/src/mod_auth_openidc.c
+++ b/src/mod_auth_openidc.c
@@ -805,8 +805,8 @@ static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c,
  * set the state that is maintained between an authorization request and an authorization response
  * in a cookie in the browser that is cryptographically bound to that state
  */
-static int oidc_authorization_request_set_cookie(request_rec *r,
-		oidc_cfg *c, const char *state, oidc_proto_state_t *proto_state) {
+static int oidc_authorization_request_set_cookie(request_rec *r, oidc_cfg *c,
+		const char *state, oidc_proto_state_t *proto_state) {
 	/*
 	 * create a cookie consisting of 8 elements:
 	 * random value, original URL, original method, issuer, response_type, response_mod, prompt and timestamp
@@ -818,7 +818,7 @@ static int oidc_authorization_request_set_cookie(request_rec *r,
 
 	/*
 	 * clean expired state cookies to avoid pollution and optionally
- 	 * try to avoid the number of state cookies exceeding a max
+	 * try to avoid the number of state cookies exceeding a max
 	 */
 	int number_of_cookies = oidc_clean_expired_state_cookies(r, c, NULL);
 	int max_number_of_cookies = oidc_cfg_max_number_of_state_cookies(c);
@@ -953,6 +953,26 @@ static void oidc_log_session_expires(request_rec *r, const char *msg,
 			apr_time_sec(session_expires - apr_time_now()));
 }
 
+/*
+ * see if this is a non-browser request
+ */
+static apr_byte_t oidc_is_xml_http_request(request_rec *r) {
+
+	if ((oidc_util_hdr_in_x_requested_with_get(r) != NULL)
+			&& (apr_strnatcasecmp(oidc_util_hdr_in_x_requested_with_get(r),
+					OIDC_HTTP_HDR_VAL_XML_HTTP_REQUEST) == 0))
+		return TRUE;
+
+	if ((oidc_util_hdr_in_accept_contains(r, OIDC_CONTENT_TYPE_TEXT_HTML)
+			== FALSE) && (oidc_util_hdr_in_accept_contains(r,
+					OIDC_CONTENT_TYPE_APP_XHTML_XML) == FALSE)
+					&& (oidc_util_hdr_in_accept_contains(r,
+							OIDC_CONTENT_TYPE_ANY) == FALSE))
+		return TRUE;
+
+	return FALSE;
+}
+
 /*
  * find out which action we need to take when encountering an unauthenticated request
  */
@@ -982,9 +1002,7 @@ static int oidc_handle_unauthenticated_user(request_rec *r, oidc_cfg *c) {
 		 * won't redirect the user and thus avoid creating a state cookie
 		 * for a non-browser (= Javascript) call that will never return from the OP
 		 */
-		if ((oidc_util_hdr_in_x_requested_with_get(r) != NULL)
-				&& (apr_strnatcasecmp(oidc_util_hdr_in_x_requested_with_get(r),
-						OIDC_HTTP_HDR_VAL_XML_HTTP_REQUEST) == 0))
+		if (oidc_is_xml_http_request(r) == TRUE)
 			return HTTP_UNAUTHORIZED;
 	}
 
@@ -997,17 +1015,6 @@ static int oidc_handle_unauthenticated_user(request_rec *r, oidc_cfg *c) {
 			oidc_dir_cfg_path_scope(r));
 }
 
-/*
- * see if this is a non-browser request
- */
-static apr_byte_t oidc_is_xml_http_request(request_rec *r) {
-	if ((oidc_util_hdr_in_x_requested_with_get(r) != NULL)
-			&& (apr_strnatcasecmp(oidc_util_hdr_in_x_requested_with_get(r),
-					OIDC_HTTP_HDR_VAL_XML_HTTP_REQUEST) == 0))
-		return TRUE;
-	return FALSE;
-}
-
 /*
  * check if maximum session duration was exceeded
  */
diff --git a/src/mod_auth_openidc.h b/src/mod_auth_openidc.h
index 5162ed4..f89f392 100644
--- a/src/mod_auth_openidc.h
+++ b/src/mod_auth_openidc.h
@@ -537,7 +537,9 @@ apr_byte_t oidc_oauth_get_bearer_token(request_rec *r, const char **access_token
 #define OIDC_CONTENT_TYPE_JWT           "application/jwt"
 #define OIDC_CONTENT_TYPE_FORM_ENCODED  "application/x-www-form-urlencoded"
 #define OIDC_CONTENT_TYPE_IMAGE_PNG     "image/png"
-#define OIDC_CONTENT_TYPE_HTML          "text/html"
+#define OIDC_CONTENT_TYPE_TEXT_HTML     "text/html"
+#define OIDC_CONTENT_TYPE_APP_XHTML_XML "application/xhtml+xml"
+#define OIDC_CONTENT_TYPE_ANY           "*/*"
 
 #define OIDC_STR_SPACE         " "
 #define OIDC_STR_EQUAL         "="
@@ -560,6 +562,7 @@ apr_byte_t oidc_oauth_get_bearer_token(request_rec *r, const char **access_token
 #define OIDC_CHAR_FORWARD_SLASH '/'
 #define OIDC_CHAR_PIPE          '|'
 #define OIDC_CHAR_AMP           '&'
+#define OIDC_CHAR_SEMI_COLON    ';'
 
 #define OIDC_APP_INFO_REFRESH_TOKEN     "refresh_token"
 #define OIDC_APP_INFO_ACCESS_TOKEN      "access_token"
@@ -787,6 +790,7 @@ const char *oidc_util_hdr_in_host_get(const request_rec *r);
 void oidc_util_hdr_out_location_set(const request_rec *r, const char *value);
 const char *oidc_util_hdr_out_location_get(const request_rec *r);
 void oidc_util_hdr_err_out_add(const request_rec *r, const char *name, const char *value);
+apr_byte_t oidc_util_hdr_in_accept_contains(const request_rec *r, const char *needle);
 
 // oidc_metadata.c
 apr_byte_t oidc_metadata_provider_retrieve(request_rec *r, oidc_cfg *cfg, const char *issuer, const char *url, json_t **j_metadata, char **response);
diff --git a/src/util.c b/src/util.c
index 2fd79ec..67b2fc3 100644
--- a/src/util.c
+++ b/src/util.c
@@ -97,7 +97,7 @@ int oidc_base64url_encode(request_rec *r, char **dst, const char *src,
 			enc_len--;
 		if ((enc_len > 0) && (enc[enc_len - 1] == ','))
 			enc_len--;
-		if ((enc_len > 0) &&(enc[enc_len - 1] == ','))
+		if ((enc_len > 0) && (enc[enc_len - 1] == ','))
 			enc_len--;
 		enc[enc_len] = '\0';
 	}
@@ -320,9 +320,9 @@ char *oidc_util_unescape_string(const request_rec *r, const char *str) {
 		return NULL;
 	}
 	int counter = 0;
-	char *replaced = (char *)str;
-	while(str[counter] != '\0') {
-		if(str[counter] == '+') {
+	char *replaced = (char *) str;
+	while (str[counter] != '\0') {
+		if (str[counter] == '+') {
 			replaced[counter] = ' ';
 		}
 		counter++;
@@ -353,7 +353,7 @@ char *oidc_util_html_escape(apr_pool_t *pool, const char *s) {
 	for (i = 0; i < strlen(s); i++) {
 		for (n = 0; n < len; n++) {
 			if (s[i] == chars[n]) {
-				m = (unsigned int)strlen(replace[n]);
+				m = (unsigned int) strlen(replace[n]);
 				for (k = 0; k < m; k++)
 					r[j + k] = replace[n][k];
 				j += m;
@@ -530,12 +530,13 @@ const char *oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *cfg,
 	const char *redirect_uri = oidc_get_redirect_uri(r, cfg);
 	if (provider->issuer_specific_redirect_uri != 0) {
 		redirect_uri = apr_psprintf(r->pool, "%s%s%s=%s", redirect_uri,
-				strchr(redirect_uri ? redirect_uri : "", OIDC_CHAR_QUERY) != NULL ?
-						OIDC_STR_AMP :
-						OIDC_STR_QUERY,
-						OIDC_PROTO_ISS, oidc_util_escape_string(r, provider->issuer));
-//						OIDC_PROTO_CLIENT_ID,
-//						oidc_util_escape_string(r, provider->client_id));
+				strchr(redirect_uri ? redirect_uri : "",
+						OIDC_CHAR_QUERY) != NULL ?
+								OIDC_STR_AMP :
+								OIDC_STR_QUERY,
+								OIDC_PROTO_ISS, oidc_util_escape_string(r, provider->issuer));
+		//						OIDC_PROTO_CLIENT_ID,
+		//						oidc_util_escape_string(r, provider->client_id));
 		oidc_debug(r, "determined issuer specific redirect uri: %s",
 				redirect_uri);
 	}
@@ -1346,8 +1347,8 @@ int oidc_util_html_send(request_rec *r, const char *title,
 							on_load ? apr_psprintf(r->pool, " onload=\"%s()\"", on_load) : "",
 									html_body ? html_body : "<p></p>");
 
-	return oidc_util_http_send(r, html, strlen(html), OIDC_CONTENT_TYPE_HTML,
-			status_code);
+	return oidc_util_http_send(r, html, strlen(html),
+			OIDC_CONTENT_TYPE_TEXT_HTML, status_code);
 }
 
 static char *html_error_template_contents = NULL;
@@ -1357,7 +1358,8 @@ static char *html_error_template_contents = NULL;
  * that is relative to the Apache root directory
  */
 char *oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename) {
-	return (abs_or_rel_filename) ? ap_server_root_relative(pool, abs_or_rel_filename) : NULL;
+	return (abs_or_rel_filename) ?
+			ap_server_root_relative(pool, abs_or_rel_filename) : NULL;
 }
 
 /*
@@ -1389,7 +1391,7 @@ int oidc_util_html_send_error(request_rec *r, const char *html_template,
 							description ? description : ""));
 
 			return oidc_util_http_send(r, html, strlen(html),
-					OIDC_CONTENT_TYPE_HTML, status_code);
+					OIDC_CONTENT_TYPE_TEXT_HTML, status_code);
 		}
 	}
 
@@ -1980,8 +1982,8 @@ void oidc_util_table_add_query_encoded_params(apr_pool_t *pool,
  * create a symmetric key from a client_secret
  */
 apr_byte_t oidc_util_create_symmetric_key(request_rec *r,
-		const char *client_secret, unsigned int r_key_len, const char *hash_algo,
-		apr_byte_t set_kid, oidc_jwk_t **jwk) {
+		const char *client_secret, unsigned int r_key_len,
+		const char *hash_algo, apr_byte_t set_kid, oidc_jwk_t **jwk) {
 	oidc_jose_error_t err;
 	unsigned char *key = NULL;
 	unsigned int key_len;
@@ -2216,7 +2218,8 @@ static const char *oidc_util_hdr_in_get(const request_rec *r, const char *name)
 	return value;
 }
 
-static const char *oidc_util_hdr_in_get_left_most_only(const request_rec *r, const char *name, const char *separator) {
+static const char *oidc_util_hdr_in_get_left_most_only(const request_rec *r,
+		const char *name, const char *separator) {
 	char *last = NULL;
 	const char *value = oidc_util_hdr_in_get(r, name);
 	if (value)
@@ -2224,6 +2227,29 @@ static const char *oidc_util_hdr_in_get_left_most_only(const request_rec *r, con
 	return NULL;
 }
 
+static apr_byte_t oidc_util_hdr_in_contains(const request_rec *r,
+		const char *name, const char *separator, const char postfix_separator,
+		const char *needle) {
+	char *ctx = NULL, *elem = NULL;
+	const char *value = oidc_util_hdr_in_get(r, name);
+	apr_byte_t rc = FALSE;
+	if (value) {
+		elem = apr_strtok(apr_pstrdup(r->pool, value), separator, &ctx);
+		while (elem != NULL) {
+			while (*elem == OIDC_CHAR_SPACE)
+				elem++;
+			if ((strncmp(elem, needle, strlen(needle)) == 0)
+					&& ((elem[strlen(needle)] == '\0')
+							|| (elem[strlen(needle)] == postfix_separator))) {
+				rc = TRUE;
+				break;
+			}
+			elem = apr_strtok(NULL, separator, &ctx);
+		}
+	}
+	return rc;
+}
+
 static void oidc_util_hdr_table_set(const request_rec *r, apr_table_t *table,
 		const char *name, const char *value) {
 
@@ -2288,7 +2314,8 @@ const char *oidc_util_hdr_in_user_agent_get(const request_rec *r) {
 }
 
 const char *oidc_util_hdr_in_x_forwarded_for_get(const request_rec *r) {
-	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_FOR, OIDC_STR_COMMA);
+	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_FOR,
+			OIDC_STR_COMMA);
 }
 
 const char *oidc_util_hdr_in_content_type_get(const request_rec *r) {
@@ -2303,20 +2330,29 @@ const char *oidc_util_hdr_in_accept_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_ACCEPT);
 }
 
+apr_byte_t oidc_util_hdr_in_accept_contains(const request_rec *r,
+		const char *needle) {
+	return oidc_util_hdr_in_contains(r, OIDC_HTTP_HDR_ACCEPT, OIDC_STR_COMMA,
+			OIDC_CHAR_SEMI_COLON, needle);
+}
+
 const char *oidc_util_hdr_in_authorization_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_AUTHORIZATION);
 }
 
 const char *oidc_util_hdr_in_x_forwarded_proto_get(const request_rec *r) {
-	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_PROTO, OIDC_STR_COMMA);
+	return oidc_util_hdr_in_get_left_most_only(r,
+			OIDC_HTTP_HDR_X_FORWARDED_PROTO, OIDC_STR_COMMA);
 }
 
 const char *oidc_util_hdr_in_x_forwarded_port_get(const request_rec *r) {
-	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_PORT, OIDC_STR_COMMA);
+	return oidc_util_hdr_in_get_left_most_only(r,
+			OIDC_HTTP_HDR_X_FORWARDED_PORT, OIDC_STR_COMMA);
 }
 
 const char *oidc_util_hdr_in_x_forwarded_host_get(const request_rec *r) {
-	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_HOST, OIDC_STR_COMMA);
+	return oidc_util_hdr_in_get_left_most_only(r,
+			OIDC_HTTP_HDR_X_FORWARDED_HOST, OIDC_STR_COMMA);
 }
 
 const char *oidc_util_hdr_in_host_get(const request_rec *r) {