1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 /***************************************************************************
21  * Copyright (C) 2017-2021 ZmartZone Holding BV
22  * Copyright (C) 2013-2017 Ping Identity Corporation
23  * All rights reserved.
24  *
25  * DISCLAIMER OF WARRANTIES:
26  *
27  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
28  * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
29  * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
30  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
31  * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
32  * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
33  * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
34  * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
35  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
37  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
42  */
43 
44 #include <apr_strings.h>
45 #include <apr_base64.h>
46 #include <apr_lib.h>
47 
48 #include <httpd.h>
49 #include <http_config.h>
50 #include <http_log.h>
51 #include <http_request.h>
52 #include "http_protocol.h"
53 
54 #include <curl/curl.h>
55 
56 #include "mod_auth_openidc.h"
57 
58 #include <pcre.h>
59 #include "pcre_subst.h"
60 
61 /* hrm, should we get rid of this by adding parameters to the (3) functions? */
62 extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
63 
64 /*
65  * base64url encode a string
66  */
oidc_base64url_encode(request_rec * r,char ** dst,const char * src,int src_len,int remove_padding)67 int oidc_base64url_encode(request_rec *r, char **dst, const char *src,
68 		int src_len, int remove_padding) {
69 	if ((src == NULL) || (src_len <= 0)) {
70 		oidc_error(r, "not encoding anything; src=NULL and/or src_len<1");
71 		return -1;
72 	}
73 	unsigned int enc_len = apr_base64_encode_len(src_len);
74 	char *enc = apr_palloc(r->pool, enc_len);
75 	apr_base64_encode(enc, (const char*) src, src_len);
76 	unsigned int i = 0;
77 	while (enc[i] != '\0') {
78 		if (enc[i] == '+')
79 			enc[i] = '-';
80 		if (enc[i] == '/')
81 			enc[i] = '_';
82 		if (enc[i] == '=')
83 			enc[i] = ',';
84 		i++;
85 	}
86 	if (remove_padding) {
87 		/* remove /0 and padding */
88 		if (enc_len > 0)
89 			enc_len--;
90 		if ((enc_len > 0) && (enc[enc_len - 1] == ','))
91 			enc_len--;
92 		if ((enc_len > 0) && (enc[enc_len - 1] == ','))
93 			enc_len--;
94 		enc[enc_len] = '\0';
95 	}
96 	*dst = enc;
97 	return enc_len;
98 }
99 
100 /*
101  * base64url decode a string
102  */
oidc_base64url_decode(apr_pool_t * pool,char ** dst,const char * src)103 int oidc_base64url_decode(apr_pool_t *pool, char **dst, const char *src) {
104 	if (src == NULL) {
105 		return -1;
106 	}
107 	char *dec = apr_pstrdup(pool, src);
108 	int i = 0;
109 	while (dec[i] != '\0') {
110 		if (dec[i] == '-')
111 			dec[i] = '+';
112 		if (dec[i] == '_')
113 			dec[i] = '/';
114 		if (dec[i] == ',')
115 			dec[i] = '=';
116 		i++;
117 	}
118 	switch (strlen(dec) % 4) {
119 	case 0:
120 		break;
121 	case 2:
122 		dec = apr_pstrcat(pool, dec, "==", NULL);
123 		break;
124 	case 3:
125 		dec = apr_pstrcat(pool, dec, "=", NULL);
126 		break;
127 	default:
128 		return 0;
129 	}
130 	int dlen = apr_base64_decode_len(dec);
131 	*dst = apr_palloc(pool, dlen);
132 	return apr_base64_decode(*dst, dec);
133 }
134 
oidc_util_jwt_create(request_rec * r,const char * secret,json_t * payload,char ** compact_encoded_jwt)135 apr_byte_t oidc_util_jwt_create(request_rec *r, const char *secret,
136 		json_t *payload, char **compact_encoded_jwt) {
137 
138 	apr_byte_t rv = FALSE;
139 	oidc_jose_error_t err;
140 
141 	oidc_jwk_t *jwk = NULL;
142 	oidc_jwt_t *jwt = NULL;
143 	oidc_jwt_t *jwe = NULL;
144 
145 	if (oidc_util_create_symmetric_key(r, secret, 0, OIDC_JOSE_ALG_SHA256,
146 			FALSE, &jwk) == FALSE)
147 		goto end;
148 
149 	jwt = oidc_jwt_new(r->pool, TRUE, FALSE);
150 	if (jwt == NULL) {
151 		oidc_error(r, "creating JWT failed");
152 		goto end;
153 	}
154 
155 	jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_HS256);
156 	jwt->payload.value.json = payload;
157 
158 	if (oidc_jwt_sign(r->pool, jwt, jwk, &err) == FALSE) {
159 		oidc_error(r, "signing JWT failed: %s", oidc_jose_e2s(r->pool, err));
160 		goto end;
161 	}
162 
163 	jwe = oidc_jwt_new(r->pool, TRUE, FALSE);
164 	if (jwe == NULL) {
165 		oidc_error(r, "creating JWE failed");
166 		goto end;
167 	}
168 
169 	jwe->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_DIR);
170 	jwe->header.enc = apr_pstrdup(r->pool, CJOSE_HDR_ENC_A256GCM);
171 
172 	const char *cser = oidc_jwt_serialize(r->pool, jwt, &err);
173 	if (oidc_jwt_encrypt(r->pool, jwe, jwk, cser, compact_encoded_jwt, &err)
174 			== FALSE) {
175 		oidc_error(r, "encrypting JWT failed: %s", oidc_jose_e2s(r->pool, err));
176 		goto end;
177 	}
178 
179 	rv = TRUE;
180 
181 end:
182 
183 	if (jwe != NULL)
184 		oidc_jwt_destroy(jwe);
185 	if (jwk != NULL)
186 		oidc_jwk_destroy(jwk);
187 	if (jwt != NULL) {
188 		jwt->payload.value.json = NULL;
189 		oidc_jwt_destroy(jwt);
190 	}
191 
192 	return rv;
193 }
194 
oidc_util_jwt_verify(request_rec * r,const char * secret,const char * compact_encoded_jwt,json_t ** result)195 apr_byte_t oidc_util_jwt_verify(request_rec *r, const char *secret,
196 		const char *compact_encoded_jwt, json_t **result) {
197 
198 	oidc_debug(r, "enter: JWT header=%s",
199 			oidc_proto_peek_jwt_header(r, compact_encoded_jwt, NULL));
200 
201 	apr_byte_t rv = FALSE;
202 	oidc_jose_error_t err;
203 
204 	oidc_jwk_t *jwk = NULL;
205 	oidc_jwt_t *jwt = NULL;
206 
207 	if (oidc_util_create_symmetric_key(r, secret, 0, OIDC_JOSE_ALG_SHA256,
208 			FALSE, &jwk) == FALSE)
209 		goto end;
210 
211 	apr_hash_t *keys = apr_hash_make(r->pool);
212 	apr_hash_set(keys, "", APR_HASH_KEY_STRING, jwk);
213 
214 	if (oidc_jwt_parse(r->pool, compact_encoded_jwt, &jwt, keys, &err)
215 			== FALSE) {
216 		oidc_error(r, "parsing JWT failed: %s", oidc_jose_e2s(r->pool, err));
217 		goto end;
218 	}
219 
220 	if (oidc_jwt_verify(r->pool, jwt, keys, &err) == FALSE) {
221 		oidc_error(r, "verifying JWT failed: %s", oidc_jose_e2s(r->pool, err));
222 		goto end;
223 	}
224 
225 	*result = json_deep_copy(jwt->payload.value.json);
226 
227 	rv = TRUE;
228 
229 end:
230 
231 	if (jwk != NULL)
232 		oidc_jwk_destroy(jwk);
233 	if (jwt != NULL)
234 		oidc_jwt_destroy(jwt);
235 
236 	return rv;
237 }
238 
239 /*
240  * convert a character to an ENVIRONMENT-variable-safe variant
241  */
oidc_char_to_env(int c)242 int oidc_char_to_env(int c) {
243 	return apr_isalnum(c) ? apr_toupper(c) : '_';
244 }
245 
246 /*
247  * compare two strings based on how they would be converted to an
248  * environment variable, as per oidc_char_to_env. If len is specified
249  * as less than zero, then the full strings will be compared. Returns
250  * less than, equal to, or greater than zero based on whether the
251  * first argument's conversion to an environment variable is less
252  * than, equal to, or greater than the second.
253  */
oidc_strnenvcmp(const char * a,const char * b,int len)254 int oidc_strnenvcmp(const char *a, const char *b, int len) {
255 	int d, i = 0;
256 	while (1) {
257 		/* If len < 0 then we don't stop based on length */
258 		if (len >= 0 && i >= len)
259 			return 0;
260 
261 		/* If we're at the end of both strings, they're equal */
262 		if (!*a && !*b)
263 			return 0;
264 
265 		/* If the second string is shorter, pick it: */
266 		if (*a && !*b)
267 			return 1;
268 
269 		/* If the first string is shorter, pick it: */
270 		if (!*a && *b)
271 			return -1;
272 
273 		/* Normalize the characters as for conversion to an
274 		 * environment variable. */
275 		d = oidc_char_to_env(*a) - oidc_char_to_env(*b);
276 		if (d)
277 			return d;
278 
279 		a++;
280 		b++;
281 		i++;
282 	}
283 	return 0;
284 }
285 
286 /*
287  * escape a string
288  */
oidc_util_escape_string(const request_rec * r,const char * str)289 char* oidc_util_escape_string(const request_rec *r, const char *str) {
290 	CURL *curl = curl_easy_init();
291 	if (curl == NULL) {
292 		oidc_error(r, "curl_easy_init() error");
293 		return NULL;
294 	}
295 	char *result = curl_easy_escape(curl, str, 0);
296 	if (result == NULL) {
297 		oidc_error(r, "curl_easy_escape() error");
298 		return NULL;
299 	}
300 	char *rv = apr_pstrdup(r->pool, result);
301 	curl_free(result);
302 	curl_easy_cleanup(curl);
303 	return rv;
304 }
305 
306 /*
307  * escape a string
308  */
oidc_util_unescape_string(const request_rec * r,const char * str)309 char* oidc_util_unescape_string(const request_rec *r, const char *str) {
310 	CURL *curl = curl_easy_init();
311 	if (curl == NULL) {
312 		oidc_error(r, "curl_easy_init() error");
313 		return NULL;
314 	}
315 	int counter = 0;
316 	char *replaced = (char*) str;
317 	while (str[counter] != '\0') {
318 		if (str[counter] == '+') {
319 			replaced[counter] = ' ';
320 		}
321 		counter++;
322 	}
323 	char *result = curl_easy_unescape(curl, replaced, 0, 0);
324 	if (result == NULL) {
325 		oidc_error(r, "curl_easy_unescape() error");
326 		return NULL;
327 	}
328 	char *rv = apr_pstrdup(r->pool, result);
329 	curl_free(result);
330 	curl_easy_cleanup(curl);
331 	//oidc_debug(r, "input=\"%s\", output=\"%s\"", str, rv);
332 	return rv;
333 }
334 
335 /*
336  * HTML escape a string
337  */
oidc_util_html_escape(apr_pool_t * pool,const char * s)338 char* oidc_util_html_escape(apr_pool_t *pool, const char *s) {
339 	// TODO: this has performance/memory issues for large chunks of HTML
340 	const char chars[6] = { '&', '\'', '\"', '>', '<', '\0' };
341 	const char *const replace[] =
342 	{ "&amp;", "&apos;", "&quot;", "&gt;", "&lt;", };
343 	unsigned int i, j = 0, k, n = 0, len = strlen(chars);
344 	unsigned int m = 0;
345 	char *r = apr_pcalloc(pool, strlen(s) * 6);
346 	for (i = 0; i < strlen(s); i++) {
347 		for (n = 0; n < len; n++) {
348 			if (s[i] == chars[n]) {
349 				m = (unsigned int) strlen(replace[n]);
350 				for (k = 0; k < m; k++)
351 					r[j + k] = replace[n][k];
352 				j += m;
353 				break;
354 			}
355 		}
356 		if (n == len) {
357 			r[j] = s[i];
358 			j++;
359 		}
360 	}
361 	r[j] = '\0';
362 	return apr_pstrdup(pool, r);
363 }
364 
365 /*
366  * get the URL scheme that is currently being accessed
367  */
oidc_get_current_url_scheme(const request_rec * r)368 static const char* oidc_get_current_url_scheme(const request_rec *r) {
369 	/* first see if there's a proxy/load-balancer in front of us */
370 	const char *scheme_str = oidc_util_hdr_in_x_forwarded_proto_get(r);
371 	/* if not we'll determine the scheme used to connect to this server */
372 	if (scheme_str == NULL) {
373 #ifdef APACHE2_0
374 		scheme_str = (char *) ap_http_method(r);
375 #else
376 		scheme_str = (char*) ap_http_scheme(r);
377 #endif
378 	}
379 	if ((scheme_str == NULL)
380 			|| ((apr_strnatcmp(scheme_str, "http") != 0)
381 					&& (apr_strnatcmp(scheme_str, "https") != 0))) {
382 		oidc_warn(r,
383 				"detected HTTP scheme \"%s\" is not \"http\" nor \"https\"; perhaps your reverse proxy passes a wrongly configured \"%s\" header: falling back to default \"https\"",
384 				scheme_str, OIDC_HTTP_HDR_X_FORWARDED_PROTO);
385 		scheme_str = "https";
386 	}
387 	return scheme_str;
388 }
389 
390 /*
391  * get the URL port that is currently being accessed
392  */
oidc_get_current_url_port(const request_rec * r,const char * scheme_str)393 static const char* oidc_get_current_url_port(const request_rec *r,
394 		const char *scheme_str) {
395 
396 	/*
397 	 * first see if there's a proxy/load-balancer in front of us
398 	 * that sets X-Forwarded-Port
399 	 */
400 	const char *port_str = oidc_util_hdr_in_x_forwarded_port_get(r);
401 	if (port_str)
402 		return port_str;
403 
404 	/*
405 	 * see if we can get the port from the "X-Forwarded-Host" header
406 	 * and if that header was set we'll assume defaults
407 	 */
408 	const char *host_hdr = oidc_util_hdr_in_x_forwarded_host_get(r);
409 	if (host_hdr) {
410 		port_str = strchr(host_hdr, OIDC_CHAR_COLON);
411 		if (port_str)
412 			port_str++;
413 		return port_str;
414 	}
415 
416 	/*
417 	 * see if we can get the port from the "Host" header; if not
418 	 * we'll determine the port locally
419 	 */
420 	host_hdr = oidc_util_hdr_in_host_get(r);
421 	if (host_hdr) {
422 		port_str = strchr(host_hdr, OIDC_CHAR_COLON);
423 		if (port_str) {
424 			port_str++;
425 			return port_str;
426 		}
427 	}
428 
429 	/*
430 	 * if X-Forwarded-Proto assume the default port otherwise the
431 	 * port should have been set in the X-Forwarded-Port header
432 	 */
433 	if (oidc_util_hdr_in_x_forwarded_proto_get(r))
434 		return NULL;
435 
436 	/*
437 	 * if no port was set in the Host header and no X-Forwarded-Proto was set, we'll
438 	 * determine the port locally and don't print it when it's the default for the protocol
439 	 */
440 	const apr_port_t port = r->connection->local_addr->port;
441 	if ((apr_strnatcmp(scheme_str, "https") == 0) && port == 443)
442 		return NULL;
443 	else if ((apr_strnatcmp(scheme_str, "http") == 0) && port == 80)
444 		return NULL;
445 
446 	port_str = apr_psprintf(r->pool, "%u", port);
447 	return port_str;
448 }
449 
450 /*
451  * get the hostname part of the URL that is currently being accessed
452  */
oidc_get_current_url_host(request_rec * r)453 const char* oidc_get_current_url_host(request_rec *r) {
454 	const char *host_str = oidc_util_hdr_in_x_forwarded_host_get(r);
455 	if (host_str == NULL)
456 		host_str = oidc_util_hdr_in_host_get(r);
457 	if (host_str) {
458 		host_str = apr_pstrdup(r->pool, host_str);
459 		char *p = strchr(host_str, OIDC_CHAR_COLON);
460 		if (p != NULL)
461 			*p = '\0';
462 	} else {
463 		/* no Host header, HTTP 1.0 */
464 		host_str = ap_get_server_name(r);
465 	}
466 	return host_str;
467 }
468 
469 /*
470  * get the base part of the current URL (scheme + host (+ port))
471  */
oidc_get_current_url_base(request_rec * r)472 static const char* oidc_get_current_url_base(request_rec *r) {
473 
474 	const char *scheme_str = oidc_get_current_url_scheme(r);
475 	const char *host_str = oidc_get_current_url_host(r);
476 	const char *port_str = oidc_get_current_url_port(r, scheme_str);
477 	port_str = port_str ? apr_psprintf(r->pool, ":%s", port_str) : "";
478 
479 	char *url = apr_pstrcat(r->pool, scheme_str, "://", host_str, port_str,
480 			NULL);
481 
482 	return url;
483 }
484 
485 /*
486  * get the URL that is currently being accessed
487  */
oidc_get_current_url(request_rec * r)488 char* oidc_get_current_url(request_rec *r) {
489 	char *url = NULL, *path = NULL;
490 	apr_uri_t uri;
491 
492 	path = r->uri;
493 
494 	/* check if we're dealing with a forward proxying secenario i.e. a non-relative URL */
495 	if ((path) && (path[0] != '/')) {
496 		memset(&uri, 0, sizeof(apr_uri_t));
497 		if (apr_uri_parse(r->pool, r->uri, &uri) == APR_SUCCESS)
498 			path = apr_pstrcat(r->pool, uri.path,
499 					(r->args != NULL && *r->args != '\0' ? "?" : ""), r->args,
500 					NULL);
501 		else
502 			oidc_warn(r, "apr_uri_parse failed on non-relative URL: %s",
503 					r->uri);
504 	} else {
505 		/* make sure we retain URL-encoded characters original URL that we send the user back to */
506 		path = r->unparsed_uri;
507 	}
508 
509 	url = apr_pstrcat(r->pool, oidc_get_current_url_base(r), path, NULL);
510 
511 	oidc_debug(r, "current URL '%s'", url);
512 
513 	return url;
514 }
515 
516 /*
517  * determine absolute redirect uri
518  */
oidc_get_redirect_uri(request_rec * r,oidc_cfg * cfg)519 const char* oidc_get_redirect_uri(request_rec *r, oidc_cfg *cfg) {
520 
521 	char *redirect_uri = cfg->redirect_uri;
522 
523 	if ((redirect_uri != NULL)
524 			&& (redirect_uri[0] == OIDC_CHAR_FORWARD_SLASH)) {
525 		// relative redirect uri
526 
527 		redirect_uri = apr_pstrcat(r->pool, oidc_get_current_url_base(r),
528 				cfg->redirect_uri, NULL);
529 
530 		oidc_debug(r, "determined absolute redirect uri: %s", redirect_uri);
531 	}
532 	return redirect_uri;
533 }
534 
535 /*
536  * determine absolute redirect uri that is issuer specific
537  */
oidc_get_redirect_uri_iss(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider)538 const char* oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *cfg,
539 		oidc_provider_t *provider) {
540 	const char *redirect_uri = oidc_get_redirect_uri(r, cfg);
541 	if (provider->issuer_specific_redirect_uri != 0) {
542 		redirect_uri = apr_psprintf(r->pool, "%s%s%s=%s", redirect_uri,
543 				strchr(redirect_uri ? redirect_uri : "",
544 						OIDC_CHAR_QUERY) != NULL ?
545 								OIDC_STR_AMP :
546 								OIDC_STR_QUERY,
547 								OIDC_PROTO_ISS, oidc_util_escape_string(r, provider->issuer));
548 		//						OIDC_PROTO_CLIENT_ID,
549 		//						oidc_util_escape_string(r, provider->client_id));
550 		oidc_debug(r, "determined issuer specific redirect uri: %s",
551 				redirect_uri);
552 	}
553 	return redirect_uri;
554 }
555 
556 /* buffer to hold HTTP call responses */
557 typedef struct oidc_curl_buffer {
558 	request_rec *r;
559 	char *memory;
560 	size_t size;
561 } oidc_curl_buffer;
562 
563 /* maximum acceptable size of HTTP responses: 1 Mb */
564 #define OIDC_CURL_MAX_RESPONSE_SIZE 1024 * 1024
565 
566 /*
567  * callback for CURL to write bytes that come back from an HTTP call
568  */
oidc_curl_write(void * contents,size_t size,size_t nmemb,void * userp)569 size_t oidc_curl_write(void *contents, size_t size, size_t nmemb, void *userp) {
570 	size_t realsize = size * nmemb;
571 	oidc_curl_buffer *mem = (oidc_curl_buffer*) userp;
572 
573 	/* check if we don't run over the maximum buffer/memory size for HTTP responses */
574 	if (mem->size + realsize > OIDC_CURL_MAX_RESPONSE_SIZE) {
575 		oidc_error(mem->r,
576 				"HTTP response larger than maximum allowed size: current size=%ld, additional size=%ld, max=%d",
577 				mem->size, realsize, OIDC_CURL_MAX_RESPONSE_SIZE);
578 		return 0;
579 	}
580 
581 	/* allocate the new buffer for the current + new response bytes */
582 	char *newptr = apr_palloc(mem->r->pool, mem->size + realsize + 1);
583 	if (newptr == NULL) {
584 		oidc_error(mem->r,
585 				"memory allocation for new buffer of %ld bytes failed",
586 				mem->size + realsize + 1);
587 		return 0;
588 	}
589 
590 	/* copy over the data from current memory plus the cURL buffer */
591 	memcpy(newptr, mem->memory, mem->size);
592 	memcpy(&(newptr[mem->size]), contents, realsize);
593 	mem->size += realsize;
594 	mem->memory = newptr;
595 	mem->memory[mem->size] = 0;
596 
597 	return realsize;
598 }
599 
600 /* context structure for encoding parameters */
601 typedef struct oidc_http_encode_t {
602 	request_rec *r;
603 	char *encoded_params;
604 } oidc_http_encode_t;
605 
606 /*
607  * add a url-form-encoded name/value pair
608  */
oidc_util_http_add_form_url_encoded_param(void * rec,const char * key,const char * value)609 static int oidc_util_http_add_form_url_encoded_param(void *rec, const char *key,
610 		const char *value) {
611 	oidc_http_encode_t *ctx = (oidc_http_encode_t*) rec;
612 	oidc_debug(ctx->r, "processing: %s=%s", key,
613 			(strncmp(key, OIDC_PROTO_CLIENT_SECRET, strlen(OIDC_PROTO_CLIENT_SECRET)) == 0) ? "***" : value);
614 	const char *sep = ctx->encoded_params ? OIDC_STR_AMP : "";
615 	ctx->encoded_params = apr_psprintf(ctx->r->pool, "%s%s%s=%s",
616 			ctx->encoded_params ? ctx->encoded_params : "", sep,
617 					oidc_util_escape_string(ctx->r, key),
618 					oidc_util_escape_string(ctx->r, value));
619 	return 1;
620 }
621 
622 /*
623  * construct a URL with query parameters
624  */
oidc_util_http_query_encoded_url(request_rec * r,const char * url,const apr_table_t * params)625 char* oidc_util_http_query_encoded_url(request_rec *r, const char *url,
626 		const apr_table_t *params) {
627 	char *result = NULL;
628 	if ((params != NULL) && (apr_table_elts(params)->nelts > 0)) {
629 		oidc_http_encode_t data = { r, NULL };
630 		apr_table_do(oidc_util_http_add_form_url_encoded_param, &data, params,
631 				NULL);
632 		const char *sep = NULL;
633 		if (data.encoded_params)
634 			sep = strchr(url ? url : "", OIDC_CHAR_QUERY) != NULL ?
635 					OIDC_STR_AMP :
636 					OIDC_STR_QUERY;
637 		result = apr_psprintf(r->pool, "%s%s%s", url, sep ? sep : "",
638 				data.encoded_params ? data.encoded_params : "");
639 	} else {
640 		result = apr_pstrdup(r->pool, url);
641 	}
642 	oidc_debug(r, "url=%s", result);
643 	return result;
644 }
645 
646 /*
647  * construct form-encoded POST data
648  */
oidc_util_http_form_encoded_data(request_rec * r,const apr_table_t * params)649 char* oidc_util_http_form_encoded_data(request_rec *r,
650 		const apr_table_t *params) {
651 	char *data = NULL;
652 	if ((params != NULL) && (apr_table_elts(params)->nelts > 0)) {
653 		oidc_http_encode_t encode_data = { r, NULL };
654 		apr_table_do(oidc_util_http_add_form_url_encoded_param, &encode_data,
655 				params,
656 				NULL);
657 		data = encode_data.encoded_params;
658 	}
659 	oidc_debug(r, "data=%s", data);
660 	return data;
661 }
662 
663 /*
664  * execute a HTTP (GET or POST) request
665  */
oidc_util_http_call(request_rec * r,const char * url,const char * data,const char * content_type,const char * basic_auth,const char * bearer_token,int ssl_validate_server,char ** response,int timeout,const char * outgoing_proxy,apr_array_header_t * pass_cookies,const char * ssl_cert,const char * ssl_key)666 static apr_byte_t oidc_util_http_call(request_rec *r, const char *url,
667 		const char *data, const char *content_type, const char *basic_auth,
668 		const char *bearer_token, int ssl_validate_server, char **response,
669 		int timeout, const char *outgoing_proxy,
670 		apr_array_header_t *pass_cookies, const char *ssl_cert,
671 		const char *ssl_key) {
672 	char curlError[CURL_ERROR_SIZE];
673 	oidc_curl_buffer curlBuffer;
674 	CURL *curl;
675 	struct curl_slist *h_list = NULL;
676 	int i;
677 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
678 			&auth_openidc_module);
679 
680 	/* do some logging about the inputs */
681 	oidc_debug(r,
682 			"url=%s, data=%s, content_type=%s, basic_auth=%s, bearer_token=%s, ssl_validate_server=%d, timeout=%d, outgoing_proxy=%s, pass_cookies=%pp, ssl_cert=%s, ssl_key=%s",
683 			url, data, content_type, basic_auth ? "****" : "null", bearer_token,
684 					ssl_validate_server, timeout, outgoing_proxy, pass_cookies,
685 					ssl_cert, ssl_key);
686 
687 	curl = curl_easy_init();
688 	if (curl == NULL) {
689 		oidc_error(r, "curl_easy_init() error");
690 		return FALSE;
691 	}
692 
693 	/* set the error buffer as empty before performing a request */
694 	curlError[0] = 0;
695 
696 	/* some of these are not really required */
697 	curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
698 	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
699 	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
700 	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlError);
701 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
702 	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
703 
704 	/* set the timeout */
705 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
706 
707 	/* setup the buffer where the response will be written to */
708 	curlBuffer.r = r;
709 	curlBuffer.memory = NULL;
710 	curlBuffer.size = 0;
711 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, oidc_curl_write);
712 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void* )&curlBuffer);
713 
714 #ifndef LIBCURL_NO_CURLPROTO
715 	curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS,
716 			CURLPROTO_HTTP|CURLPROTO_HTTPS);
717 	curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
718 #endif
719 
720 	/* set the options for validating the SSL server certificate that the remote site presents */
721 	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,
722 			(ssl_validate_server != FALSE ? 1L : 0L));
723 	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
724 			(ssl_validate_server != FALSE ? 2L : 0L));
725 
726 #if LIBCURL_VERSION_NUM >= 0x071900
727 	if (r->subprocess_env != NULL) {
728 		const char *env_var_value = apr_table_get(r->subprocess_env,
729 				"CURLOPT_SSL_OPTIONS");
730 		if (env_var_value != NULL) {
731 			oidc_debug(r, "SSL options environment variable %s=%s found",
732 					"CURLOPT_SSL_OPTIONS", env_var_value);
733 			if (strstr(env_var_value, "CURLSSLOPT_ALLOW_BEAST")) {
734 				oidc_debug(r,
735 						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_ALLOW_BEAST");
736 				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
737 						CURLSSLOPT_ALLOW_BEAST);
738 			}
739 #if LIBCURL_VERSION_NUM >= 0x072c00
740 			if (strstr(env_var_value, "CURLSSLOPT_NO_REVOKE")) {
741 				oidc_debug(r,
742 						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NO_REVOKE");
743 				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
744 						CURLSSLOPT_NO_REVOKE);
745 			}
746 #endif
747 #if LIBCURL_VERSION_NUM >= 0x074400
748 			if (strstr(env_var_value, "CURLSSLOPT_NO_PARTIALCHAIN")) {
749 				oidc_debug(r,
750 						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NO_PARTIALCHAIN");
751 				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
752 						CURLSSLOPT_NO_PARTIALCHAIN);
753 			}
754 #endif
755 #if LIBCURL_VERSION_NUM >= 0x074600
756 			if (strstr(env_var_value, "CURLSSLOPT_REVOKE_BEST_EFFORT")) {
757 				oidc_debug(r,
758 						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_REVOKE_BEST_EFFORT");
759 				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
760 						CURLSSLOPT_REVOKE_BEST_EFFORT);
761 			}
762 #endif
763 #if LIBCURL_VERSION_NUM >= 0x074700
764 			if (strstr(env_var_value, "CURLSSLOPT_NATIVE_CA")) {
765 				oidc_debug(r,
766 						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NATIVE_CA");
767 				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
768 						CURLSSLOPT_NATIVE_CA);
769 			}
770 #endif
771 		}
772 	}
773 #endif
774 
775 	if (c->ca_bundle_path != NULL)
776 		curl_easy_setopt(curl, CURLOPT_CAINFO, c->ca_bundle_path);
777 
778 #ifdef WIN32
779 	else {
780 		DWORD buflen;
781 		char *ptr = NULL;
782 		char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
783 		retval[0] = '\0';
784 		buflen = SearchPath(NULL, "curl-ca-bundle.crt", NULL, MAX_PATH+1, retval, &ptr);
785 		if (buflen > 0)
786 			curl_easy_setopt(curl, CURLOPT_CAINFO, retval);
787 		else
788 			oidc_warn(r, "no curl-ca-bundle.crt file found in path");
789 		free(retval);
790 	}
791 #endif
792 
793 	/* identify this HTTP client */
794 	curl_easy_setopt(curl, CURLOPT_USERAGENT, "mod_auth_openidc");
795 
796 	/* set optional outgoing proxy for the local network */
797 	if (outgoing_proxy) {
798 		curl_easy_setopt(curl, CURLOPT_PROXY, outgoing_proxy);
799 	}
800 
801 	/* see if we need to add token in the Bearer Authorization header */
802 	if (bearer_token != NULL) {
803 		h_list = curl_slist_append(h_list,
804 				apr_psprintf(r->pool, "Authorization: Bearer %s",
805 						bearer_token));
806 	}
807 
808 	/* see if we need to perform HTTP basic authentication to the remote site */
809 	if (basic_auth != NULL) {
810 		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
811 		curl_easy_setopt(curl, CURLOPT_USERPWD, basic_auth);
812 	}
813 
814 	if (ssl_cert != NULL)
815 		curl_easy_setopt(curl, CURLOPT_SSLCERT, ssl_cert);
816 	if (ssl_key != NULL)
817 		curl_easy_setopt(curl, CURLOPT_SSLKEY, ssl_key);
818 
819 	if (data != NULL) {
820 		/* set POST data */
821 		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
822 		/* set HTTP method to POST */
823 		curl_easy_setopt(curl, CURLOPT_POST, 1);
824 	}
825 
826 	if (content_type != NULL) {
827 		/* set content type */
828 		h_list = curl_slist_append(h_list,
829 				apr_psprintf(r->pool, "%s: %s", OIDC_HTTP_HDR_CONTENT_TYPE,
830 						content_type));
831 	}
832 
833 	/* see if we need to add any custom headers */
834 	if (h_list != NULL)
835 		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, h_list);
836 
837 	if (pass_cookies != NULL) {
838 		/* gather cookies that we need to pass on from the incoming request */
839 		char *cookie_string = NULL;
840 		for (i = 0; i < pass_cookies->nelts; i++) {
841 			const char *cookie_name = ((const char**) pass_cookies->elts)[i];
842 			char *cookie_value = oidc_util_get_cookie(r, cookie_name);
843 			if (cookie_value != NULL) {
844 				cookie_string =
845 						(cookie_string == NULL) ?
846 								apr_psprintf(r->pool, "%s=%s", cookie_name,
847 										cookie_value) :
848 										apr_psprintf(r->pool, "%s; %s=%s",
849 												cookie_string, cookie_name,
850 												cookie_value);
851 			}
852 		}
853 
854 		/* see if we need to pass any cookies */
855 		if (cookie_string != NULL) {
856 			oidc_debug(r, "passing browser cookies on backend call: %s",
857 					cookie_string);
858 			curl_easy_setopt(curl, CURLOPT_COOKIE, cookie_string);
859 		}
860 	}
861 
862 	/* set the target URL */
863 	curl_easy_setopt(curl, CURLOPT_URL, url);
864 
865 	/* call it and record the result */
866 	int rv = TRUE;
867 	if (curl_easy_perform(curl) != CURLE_OK) {
868 		oidc_error(r, "curl_easy_perform() failed on: %s (%s)", url,
869 				curlError[0] ? curlError : "");
870 		rv = FALSE;
871 		goto out;
872 	}
873 
874 	long response_code;
875 	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
876 	oidc_debug(r, "HTTP response code=%ld", response_code);
877 
878 	*response = apr_pstrmemdup(r->pool, curlBuffer.memory, curlBuffer.size);
879 
880 	/* set and log the response */
881 	oidc_debug(r, "response=%s", *response ? *response : "");
882 
883 out:
884 
885 	/* cleanup and return the result */
886 	if (h_list != NULL)
887 		curl_slist_free_all(h_list);
888 	curl_easy_cleanup(curl);
889 
890 	return rv;
891 }
892 
893 /*
894  * execute HTTP GET request
895  */
oidc_util_http_get(request_rec * r,const char * url,const apr_table_t * params,const char * basic_auth,const char * bearer_token,int ssl_validate_server,char ** response,int timeout,const char * outgoing_proxy,apr_array_header_t * pass_cookies,const char * ssl_cert,const char * ssl_key)896 apr_byte_t oidc_util_http_get(request_rec *r, const char *url,
897 		const apr_table_t *params, const char *basic_auth,
898 		const char *bearer_token, int ssl_validate_server, char **response,
899 		int timeout, const char *outgoing_proxy,
900 		apr_array_header_t *pass_cookies, const char *ssl_cert,
901 		const char *ssl_key) {
902 	char *query_url = oidc_util_http_query_encoded_url(r, url, params);
903 	return oidc_util_http_call(r, query_url, NULL, NULL, basic_auth,
904 			bearer_token, ssl_validate_server, response, timeout,
905 			outgoing_proxy, pass_cookies, ssl_cert, ssl_key);
906 }
907 
908 /*
909  * execute HTTP POST request with form-encoded data
910  */
oidc_util_http_post_form(request_rec * r,const char * url,const apr_table_t * params,const char * basic_auth,const char * bearer_token,int ssl_validate_server,char ** response,int timeout,const char * outgoing_proxy,apr_array_header_t * pass_cookies,const char * ssl_cert,const char * ssl_key)911 apr_byte_t oidc_util_http_post_form(request_rec *r, const char *url,
912 		const apr_table_t *params, const char *basic_auth,
913 		const char *bearer_token, int ssl_validate_server, char **response,
914 		int timeout, const char *outgoing_proxy,
915 		apr_array_header_t *pass_cookies, const char *ssl_cert,
916 		const char *ssl_key) {
917 	char *data = oidc_util_http_form_encoded_data(r, params);
918 	return oidc_util_http_call(r, url, data,
919 			OIDC_CONTENT_TYPE_FORM_ENCODED, basic_auth, bearer_token,
920 			ssl_validate_server, response, timeout, outgoing_proxy,
921 			pass_cookies, ssl_cert, ssl_key);
922 }
923 
924 /*
925  * execute HTTP POST request with JSON-encoded data
926  */
oidc_util_http_post_json(request_rec * r,const char * url,json_t * json,const char * basic_auth,const char * bearer_token,int ssl_validate_server,char ** response,int timeout,const char * outgoing_proxy,apr_array_header_t * pass_cookies,const char * ssl_cert,const char * ssl_key)927 apr_byte_t oidc_util_http_post_json(request_rec *r, const char *url,
928 		json_t *json, const char *basic_auth, const char *bearer_token,
929 		int ssl_validate_server, char **response, int timeout,
930 		const char *outgoing_proxy, apr_array_header_t *pass_cookies,
931 		const char *ssl_cert, const char *ssl_key) {
932 	char *data =
933 			json != NULL ?
934 					oidc_util_encode_json_object(r, json, JSON_COMPACT) : NULL;
935 	return oidc_util_http_call(r, url, data, OIDC_CONTENT_TYPE_JSON, basic_auth,
936 			bearer_token, ssl_validate_server, response, timeout,
937 			outgoing_proxy, pass_cookies, ssl_cert, ssl_key);
938 }
939 
940 /*
941  * get the current path from the request in a normalized way
942  */
oidc_util_get_path(request_rec * r)943 static char* oidc_util_get_path(request_rec *r) {
944 	size_t i;
945 	char *p;
946 	p = r->parsed_uri.path;
947 	if ((p == NULL) || (p[0] == '\0'))
948 		return apr_pstrdup(r->pool, OIDC_STR_FORWARD_SLASH);
949 	for (i = strlen(p) - 1; i > 0; i--)
950 		if (p[i] == OIDC_CHAR_FORWARD_SLASH)
951 			break;
952 	return apr_pstrndup(r->pool, p, i + 1);
953 }
954 
955 /*
956  * get the cookie path setting and check that it matches the request path; cook it up if it is not set
957  */
oidc_util_get_cookie_path(request_rec * r)958 static char* oidc_util_get_cookie_path(request_rec *r) {
959 	char *rv = NULL, *requestPath = oidc_util_get_path(r);
960 	char *cookie_path = oidc_cfg_dir_cookie_path(r);
961 	if (cookie_path != NULL) {
962 		if (strncmp(cookie_path, requestPath, strlen(cookie_path)) == 0)
963 			rv = cookie_path;
964 		else {
965 			oidc_warn(r,
966 					"" OIDCCookiePath " (%s) is not a substring of request path, using request path (%s) for cookie",
967 					cookie_path, requestPath);
968 			rv = requestPath;
969 		}
970 	} else {
971 		rv = requestPath;
972 	}
973 	return (rv);
974 }
975 
976 #define OIDC_COOKIE_FLAG_DOMAIN         "Domain"
977 #define OIDC_COOKIE_FLAG_PATH           "Path"
978 #define OIDC_COOKIE_FLAG_EXPIRES        "Expires"
979 #define OIDC_COOKIE_FLAG_SECURE         "Secure"
980 #define OIDC_COOKIE_FLAG_HTTP_ONLY      "HttpOnly"
981 
982 #define OIDC_COOKIE_MAX_SIZE            4093
983 
984 #define OIDC_SET_COOKIE_APPEND_ENV_VAR  "OIDC_SET_COOKIE_APPEND"
985 
oidc_util_set_cookie_append_value(request_rec * r,oidc_cfg * c)986 const char* oidc_util_set_cookie_append_value(request_rec *r, oidc_cfg *c) {
987 	const char *env_var_value = NULL;
988 
989 	if (r->subprocess_env != NULL)
990 		env_var_value = apr_table_get(r->subprocess_env,
991 				OIDC_SET_COOKIE_APPEND_ENV_VAR);
992 
993 	if (env_var_value == NULL) {
994 		oidc_debug(r, "no cookie append environment variable %s found",
995 				OIDC_SET_COOKIE_APPEND_ENV_VAR);
996 		return NULL;
997 	}
998 
999 	oidc_debug(r, "cookie append environment variable %s=%s found",
1000 			OIDC_SET_COOKIE_APPEND_ENV_VAR, env_var_value);
1001 
1002 	return env_var_value;
1003 }
1004 
oidc_util_request_is_secure(request_rec * r)1005 apr_byte_t oidc_util_request_is_secure(request_rec *r) {
1006 	return (apr_strnatcasecmp("https", oidc_get_current_url_scheme(r)) == 0);
1007 }
1008 
1009 /*
1010  * set a cookie in the HTTP response headers
1011  */
oidc_util_set_cookie(request_rec * r,const char * cookieName,const char * cookieValue,apr_time_t expires,const char * ext)1012 void oidc_util_set_cookie(request_rec *r, const char *cookieName,
1013 		const char *cookieValue, apr_time_t expires, const char *ext) {
1014 
1015 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
1016 			&auth_openidc_module);
1017 	char *headerString, *expiresString = NULL;
1018 	const char *appendString = NULL;
1019 
1020 	/* see if we need to clear the cookie */
1021 	if (apr_strnatcmp(cookieValue, "") == 0)
1022 		expires = 0;
1023 
1024 	/* construct the expire value */
1025 	if (expires != -1) {
1026 		expiresString = (char*) apr_pcalloc(r->pool, APR_RFC822_DATE_LEN);
1027 		if (apr_rfc822_date(expiresString, expires) != APR_SUCCESS) {
1028 			oidc_error(r, "could not set cookie expiry date");
1029 		}
1030 	}
1031 
1032 	/* construct the cookie value */
1033 	headerString = apr_psprintf(r->pool, "%s=%s", cookieName, cookieValue);
1034 
1035 	headerString = apr_psprintf(r->pool, "%s; %s=%s", headerString,
1036 			OIDC_COOKIE_FLAG_PATH, oidc_util_get_cookie_path(r));
1037 
1038 	if (expiresString != NULL)
1039 		headerString = apr_psprintf(r->pool, "%s; %s=%s", headerString,
1040 				OIDC_COOKIE_FLAG_EXPIRES, expiresString);
1041 
1042 	if (c->cookie_domain != NULL)
1043 		headerString = apr_psprintf(r->pool, "%s; %s=%s", headerString,
1044 				OIDC_COOKIE_FLAG_DOMAIN, c->cookie_domain);
1045 
1046 	if (oidc_util_request_is_secure(r))
1047 		headerString = apr_psprintf(r->pool, "%s; %s", headerString,
1048 				OIDC_COOKIE_FLAG_SECURE);
1049 
1050 	if (c->cookie_http_only != FALSE)
1051 		headerString = apr_psprintf(r->pool, "%s; %s", headerString,
1052 				OIDC_COOKIE_FLAG_HTTP_ONLY);
1053 
1054 	appendString = oidc_util_set_cookie_append_value(r, c);
1055 	if (appendString != NULL)
1056 		headerString = apr_psprintf(r->pool, "%s; %s", headerString,
1057 				appendString);
1058 	else if (ext != NULL)
1059 		headerString = apr_psprintf(r->pool, "%s; %s", headerString, ext);
1060 
1061 	/* sanity check on overall cookie value size */
1062 	if (strlen(headerString) > OIDC_COOKIE_MAX_SIZE) {
1063 		oidc_warn(r,
1064 				"the length of the cookie value (%d) is greater than %d(!) bytes, this may not work with all browsers/server combinations: consider switching to a server side caching!",
1065 				(int )strlen(headerString), OIDC_COOKIE_MAX_SIZE);
1066 	}
1067 
1068 	/* use r->err_headers_out so we always print our headers (even on 302 redirect) - headers_out only prints on 2xx responses */
1069 	oidc_util_hdr_err_out_add(r, OIDC_HTTP_HDR_SET_COOKIE, headerString);
1070 }
1071 
1072 /*
1073  * get a cookie from the HTTP request
1074  */
oidc_util_get_cookie(request_rec * r,const char * cookieName)1075 char* oidc_util_get_cookie(request_rec *r, const char *cookieName) {
1076 	char *cookie, *tokenizerCtx, *rv = NULL;
1077 
1078 	/* get the Cookie value */
1079 	char *cookies = apr_pstrdup(r->pool, oidc_util_hdr_in_cookie_get(r));
1080 
1081 	if (cookies != NULL) {
1082 
1083 		/* tokenize on ; to find the cookie we want */
1084 		cookie = apr_strtok(cookies, OIDC_STR_SEMI_COLON, &tokenizerCtx);
1085 
1086 		while (cookie != NULL) {
1087 
1088 			while (*cookie == OIDC_CHAR_SPACE)
1089 				cookie++;
1090 
1091 			/* see if we've found the cookie that we're looking for */
1092 			if ((strncmp(cookie, cookieName, strlen(cookieName)) == 0)
1093 					&& (cookie[strlen(cookieName)] == OIDC_CHAR_EQUAL)) {
1094 
1095 				/* skip to the meat of the parameter (the value after the '=') */
1096 				cookie += (strlen(cookieName) + 1);
1097 				rv = apr_pstrdup(r->pool, cookie);
1098 
1099 				break;
1100 			}
1101 
1102 			/* go to the next cookie */
1103 			cookie = apr_strtok(NULL, OIDC_STR_SEMI_COLON, &tokenizerCtx);
1104 		}
1105 	}
1106 
1107 	/* log what we've found */
1108 	oidc_debug(r, "returning \"%s\" = %s", cookieName,
1109 			rv ? apr_psprintf(r->pool, "\"%s\"", rv) : "<null>");
1110 
1111 	return rv;
1112 }
1113 
1114 #define OIDC_COOKIE_CHUNKS_SEPARATOR "_"
1115 #define OIDC_COOKIE_CHUNKS_POSTFIX "chunks"
1116 
1117 /*
1118  * get the name of the cookie that contains the number of chunks
1119  */
oidc_util_get_chunk_count_name(request_rec * r,const char * cookieName)1120 static char* oidc_util_get_chunk_count_name(request_rec *r,
1121 		const char *cookieName) {
1122 	return apr_psprintf(r->pool, "%s%s%s", cookieName,
1123 			OIDC_COOKIE_CHUNKS_SEPARATOR, OIDC_COOKIE_CHUNKS_POSTFIX);
1124 }
1125 
1126 /*
1127  * get the number of cookie chunks set by the browser
1128  */
oidc_util_get_chunked_count(request_rec * r,const char * cookieName)1129 static int oidc_util_get_chunked_count(request_rec *r, const char *cookieName) {
1130 	int chunkCount = 0;
1131 	char *chunkCountValue = oidc_util_get_cookie(r,
1132 			oidc_util_get_chunk_count_name(r, cookieName));
1133 	if (chunkCountValue != NULL) {
1134 		char *endptr = NULL;
1135 		chunkCount = strtol(chunkCountValue, &endptr, 10);
1136 		if ((*chunkCountValue == '\0') || (*endptr != '\0'))
1137 			chunkCount = 0;
1138 	}
1139 	return chunkCount;
1140 }
1141 
1142 /*
1143  * get the name of a chunk
1144  */
oidc_util_get_chunk_cookie_name(request_rec * r,const char * cookieName,int i)1145 static char* oidc_util_get_chunk_cookie_name(request_rec *r,
1146 		const char *cookieName, int i) {
1147 	return apr_psprintf(r->pool, "%s%s%d", cookieName,
1148 			OIDC_COOKIE_CHUNKS_SEPARATOR, i);
1149 }
1150 
1151 /*
1152  * get a cookie value that is split over a number of chunked cookies
1153  */
oidc_util_get_chunked_cookie(request_rec * r,const char * cookieName,int chunkSize)1154 char* oidc_util_get_chunked_cookie(request_rec *r, const char *cookieName,
1155 		int chunkSize) {
1156 	char *cookieValue = NULL;
1157 	char *chunkValue = NULL;
1158 	int i = 0;
1159 	if (chunkSize == 0) {
1160 		cookieValue = oidc_util_get_cookie(r, cookieName);
1161 	} else {
1162 		int chunkCount = oidc_util_get_chunked_count(r, cookieName);
1163 		if (chunkCount > 0) {
1164 			cookieValue = "";
1165 			for (i = 0; i < chunkCount; i++) {
1166 				chunkValue = oidc_util_get_cookie(r,
1167 						oidc_util_get_chunk_cookie_name(r, cookieName, i));
1168 				if (chunkValue != NULL)
1169 					cookieValue = apr_psprintf(r->pool, "%s%s", cookieValue,
1170 							chunkValue);
1171 			}
1172 		} else {
1173 			cookieValue = oidc_util_get_cookie(r, cookieName);
1174 		}
1175 	}
1176 	return cookieValue;
1177 }
1178 
1179 /*
1180  * unset all chunked cookies, including the counter cookie, if they exist
1181  */
oidc_util_clear_chunked_cookie(request_rec * r,const char * cookieName,apr_time_t expires,const char * ext)1182 static void oidc_util_clear_chunked_cookie(request_rec *r,
1183 		const char *cookieName, apr_time_t expires, const char *ext) {
1184 	int i = 0;
1185 	int chunkCount = oidc_util_get_chunked_count(r, cookieName);
1186 	if (chunkCount > 0) {
1187 		for (i = 0; i < chunkCount; i++)
1188 			oidc_util_set_cookie(r,
1189 					oidc_util_get_chunk_cookie_name(r, cookieName, i), "",
1190 					expires, ext);
1191 		oidc_util_set_cookie(r, oidc_util_get_chunk_count_name(r, cookieName),
1192 				"", expires, ext);
1193 	}
1194 }
1195 
1196 /*
1197  * set a cookie value that is split over a number of chunked cookies
1198  */
oidc_util_set_chunked_cookie(request_rec * r,const char * cookieName,const char * cookieValue,apr_time_t expires,int chunkSize,const char * ext)1199 void oidc_util_set_chunked_cookie(request_rec *r, const char *cookieName,
1200 		const char *cookieValue, apr_time_t expires, int chunkSize,
1201 		const char *ext) {
1202 	int i = 0;
1203 	int cookieLength = strlen(cookieValue);
1204 	char *chunkValue = NULL;
1205 
1206 	/* see if we need to chunk at all */
1207 	if ((chunkSize == 0)
1208 			|| ((cookieLength > 0) && (cookieLength < chunkSize))) {
1209 		oidc_util_set_cookie(r, cookieName, cookieValue, expires, ext);
1210 		oidc_util_clear_chunked_cookie(r, cookieName, expires, ext);
1211 		return;
1212 	}
1213 
1214 	/* see if we need to clear a possibly chunked cookie */
1215 	if (cookieLength == 0) {
1216 		oidc_util_set_cookie(r, cookieName, "", expires, ext);
1217 		oidc_util_clear_chunked_cookie(r, cookieName, expires, ext);
1218 		return;
1219 	}
1220 
1221 	/* set a chunked cookie */
1222 	int chunkCountValue = cookieLength / chunkSize + 1;
1223 	const char *ptr = cookieValue;
1224 	for (i = 0; i < chunkCountValue; i++) {
1225 		chunkValue = apr_pstrndup(r->pool, ptr, chunkSize);
1226 		ptr += chunkSize;
1227 		oidc_util_set_cookie(r,
1228 				oidc_util_get_chunk_cookie_name(r, cookieName, i), chunkValue,
1229 				expires, ext);
1230 	};
1231 	oidc_util_set_cookie(r, oidc_util_get_chunk_count_name(r, cookieName),
1232 			apr_psprintf(r->pool, "%d", chunkCountValue), expires, ext);
1233 	oidc_util_set_cookie(r, cookieName, "", expires, ext);
1234 }
1235 
1236 /*
1237  * normalize a string for use as an HTTP Header Name.  Any invalid
1238  * characters (per http://tools.ietf.org/html/rfc2616#section-4.2 and
1239  * http://tools.ietf.org/html/rfc2616#section-2.2) are replaced with
1240  * a dash ('-') character.
1241  */
oidc_normalize_header_name(const request_rec * r,const char * str)1242 char* oidc_normalize_header_name(const request_rec *r, const char *str) {
1243 	/* token = 1*<any CHAR except CTLs or separators>
1244 	 * CTL = <any US-ASCII control character
1245 	 *          (octets 0 - 31) and DEL (127)>
1246 	 * separators = "(" | ")" | "<" | ">" | "@"
1247 	 *              | "," | ";" | ":" | "\" | <">
1248 	 *              | "/" | "[" | "]" | "?" | "="
1249 	 *              | "{" | "}" | SP | HT */
1250 	const char *separators = "()<>@,;:\\\"/[]?={} \t";
1251 
1252 	char *ns = apr_pstrdup(r->pool, str);
1253 	size_t i;
1254 	for (i = 0; i < strlen(ns); i++) {
1255 		if (ns[i] < 32 || ns[i] == 127)
1256 			ns[i] = '-';
1257 		else if (strchr(separators, ns[i]) != NULL)
1258 			ns[i] = '-';
1259 	}
1260 	return ns;
1261 }
1262 
1263 /*
1264  * see if the currently accessed path matches a path from a defined URL
1265  */
oidc_util_request_matches_url(request_rec * r,const char * url)1266 apr_byte_t oidc_util_request_matches_url(request_rec *r, const char *url) {
1267 	apr_uri_t uri;
1268 	memset(&uri, 0, sizeof(apr_uri_t));
1269 	if ((url == NULL) || (apr_uri_parse(r->pool, url, &uri) != APR_SUCCESS))
1270 		return FALSE;
1271 	oidc_debug(r, "comparing \"%s\"==\"%s\"", r->parsed_uri.path, uri.path);
1272 	if ((r->parsed_uri.path == NULL) || (uri.path == NULL))
1273 		return (r->parsed_uri.path == uri.path);
1274 	return (apr_strnatcmp(r->parsed_uri.path, uri.path) == 0);
1275 }
1276 
1277 /*
1278  * see if the currently accessed path has a certain query parameter
1279  */
oidc_util_request_has_parameter(request_rec * r,const char * param)1280 apr_byte_t oidc_util_request_has_parameter(request_rec *r, const char *param) {
1281 	if (r->args == NULL)
1282 		return FALSE;
1283 	const char *option1 = apr_psprintf(r->pool, "%s=", param);
1284 	const char *option2 = apr_psprintf(r->pool, "&%s=", param);
1285 	return ((strstr(r->args, option1) == r->args)
1286 			|| (strstr(r->args, option2) != NULL)) ? TRUE : FALSE;
1287 }
1288 
1289 /*
1290  * get a query parameter
1291  */
oidc_util_get_request_parameter(request_rec * r,char * name,char ** value)1292 apr_byte_t oidc_util_get_request_parameter(request_rec *r, char *name,
1293 		char **value) {
1294 	char *tokenizer_ctx, *p, *args;
1295 	const char *k_param = apr_psprintf(r->pool, "%s=", name);
1296 	const size_t k_param_sz = strlen(k_param);
1297 
1298 	*value = NULL;
1299 
1300 	if (r->args == NULL || strlen(r->args) == 0)
1301 		return FALSE;
1302 
1303 	/* not sure why we do this, but better be safe than sorry */
1304 	args = apr_pstrmemdup(r->pool, r->args, strlen(r->args));
1305 
1306 	p = apr_strtok(args, OIDC_STR_AMP, &tokenizer_ctx);
1307 	do {
1308 		if (p && strncmp(p, k_param, k_param_sz) == 0) {
1309 			*value = apr_pstrdup(r->pool, p + k_param_sz);
1310 			*value = oidc_util_unescape_string(r, *value);
1311 		}
1312 		p = apr_strtok(NULL, OIDC_STR_AMP, &tokenizer_ctx);
1313 	} while (p);
1314 
1315 	return (*value != NULL ? TRUE : FALSE);
1316 }
1317 
1318 /*
1319  * printout a JSON string value
1320  */
oidc_util_json_string_print(request_rec * r,json_t * result,const char * key,const char * log)1321 static apr_byte_t oidc_util_json_string_print(request_rec *r, json_t *result,
1322 		const char *key, const char *log) {
1323 	json_t *value = json_object_get(result, key);
1324 	if (value != NULL && !json_is_null(value)) {
1325 		oidc_error(r,
1326 				"%s: response contained an \"%s\" entry with value: \"%s\"",
1327 				log, key,
1328 				oidc_util_encode_json_object(r, value, JSON_ENCODE_ANY));
1329 		return TRUE;
1330 	}
1331 	return FALSE;
1332 }
1333 
1334 /*
1335  * check a JSON object for "error" results and printout
1336  */
oidc_util_check_json_error(request_rec * r,json_t * json)1337 static apr_byte_t oidc_util_check_json_error(request_rec *r, json_t *json) {
1338 	if (oidc_util_json_string_print(r, json, OIDC_PROTO_ERROR,
1339 			"oidc_util_check_json_error") == TRUE) {
1340 		oidc_util_json_string_print(r, json, OIDC_PROTO_ERROR_DESCRIPTION,
1341 				"oidc_util_check_json_error");
1342 		return TRUE;
1343 	}
1344 	return FALSE;
1345 }
1346 
1347 #define OIDC_JSON_MAX_ERROR_STR 4096
1348 
1349 /*
1350  * parse a JSON object
1351  */
oidc_util_decode_json_object(request_rec * r,const char * str,json_t ** json)1352 apr_byte_t oidc_util_decode_json_object(request_rec *r, const char *str,
1353 		json_t **json) {
1354 
1355 	if (str == NULL)
1356 		return FALSE;
1357 
1358 	json_error_t json_error;
1359 	*json = json_loads(str, 0, &json_error);
1360 
1361 	/* decode the JSON contents of the buffer */
1362 	if (*json == NULL) {
1363 		/* something went wrong */
1364 #if JANSSON_VERSION_HEX >= 0x020B00
1365 		if (json_error_code(&json_error) == json_error_null_character) {
1366 			oidc_error(r, "JSON parsing returned an error: %s",
1367 					json_error.text);
1368 		} else {
1369 #endif
1370 			oidc_error(r, "JSON parsing returned an error: %s (%s)",
1371 					json_error.text,
1372 					apr_pstrndup(r->pool, str, OIDC_JSON_MAX_ERROR_STR));
1373 #if JANSSON_VERSION_HEX >= 0x020B00
1374 		}
1375 #endif
1376 		return FALSE;
1377 	}
1378 
1379 	if (!json_is_object(*json)) {
1380 		/* oops, no JSON */
1381 		oidc_error(r, "parsed JSON did not contain a JSON object");
1382 		json_decref(*json);
1383 		*json = NULL;
1384 		return FALSE;
1385 	}
1386 
1387 	return TRUE;
1388 }
1389 
1390 /*
1391  * encode a JSON object
1392  */
oidc_util_encode_json_object(request_rec * r,json_t * json,size_t flags)1393 char* oidc_util_encode_json_object(request_rec *r, json_t *json, size_t flags) {
1394 	char *s = json_dumps(json, flags);
1395 	char *s_value = apr_pstrdup(r->pool, s);
1396 	free(s);
1397 	return s_value;
1398 }
1399 
1400 /*
1401  * decode a JSON string, check for "error" results and printout
1402  */
oidc_util_decode_json_and_check_error(request_rec * r,const char * str,json_t ** json)1403 apr_byte_t oidc_util_decode_json_and_check_error(request_rec *r,
1404 		const char *str, json_t **json) {
1405 
1406 	if (oidc_util_decode_json_object(r, str, json) == FALSE)
1407 		return FALSE;
1408 
1409 	// see if it is not an error response somehow
1410 	if (oidc_util_check_json_error(r, *json) == TRUE) {
1411 		json_decref(*json);
1412 		*json = NULL;
1413 		return FALSE;
1414 	}
1415 
1416 	return TRUE;
1417 }
1418 
1419 /*
1420  * sends content to the user agent
1421  */
oidc_util_http_send(request_rec * r,const char * data,size_t data_len,const char * content_type,int success_rvalue)1422 int oidc_util_http_send(request_rec *r, const char *data, size_t data_len,
1423 		const char *content_type, int success_rvalue) {
1424 	ap_set_content_type(r, content_type);
1425 	apr_bucket_brigade *bb = apr_brigade_create(r->pool,
1426 			r->connection->bucket_alloc);
1427 	apr_bucket *b = apr_bucket_transient_create(data, data_len,
1428 			r->connection->bucket_alloc);
1429 	APR_BRIGADE_INSERT_TAIL(bb, b);
1430 	b = apr_bucket_eos_create(r->connection->bucket_alloc);
1431 	APR_BRIGADE_INSERT_TAIL(bb, b);
1432 	int rc = ap_pass_brigade(r->output_filters, bb);
1433 	if (rc != APR_SUCCESS) {
1434 		oidc_error(r,
1435 				"ap_pass_brigade returned an error: %d; if you're using this module combined with mod_deflate try make an exception for the " OIDCRedirectURI " e.g. using SetEnvIf Request_URI <url> no-gzip",
1436 				rc);
1437 		return HTTP_INTERNAL_SERVER_ERROR;
1438 	}
1439 	//r->status = success_rvalue;
1440 
1441 	if ((success_rvalue == OK) && (r->user == NULL)) {
1442 		/*
1443 		 * satisfy Apache 2.4 mod_authz_core:
1444 		 * prevent it to return HTTP 500 after sending content
1445 		 */
1446 		r->user = "";
1447 	}
1448 
1449 	return success_rvalue;
1450 }
1451 
1452 /*
1453  * send HTML content to the user agent
1454  */
oidc_util_html_send(request_rec * r,const char * title,const char * html_head,const char * on_load,const char * html_body,int status_code)1455 int oidc_util_html_send(request_rec *r, const char *title,
1456 		const char *html_head, const char *on_load, const char *html_body,
1457 		int status_code) {
1458 
1459 	char *html =
1460 			"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
1461 			"<html>\n"
1462 			"  <head>\n"
1463 			"    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
1464 			"    <title>%s</title>\n"
1465 			"    %s\n"
1466 			"  </head>\n"
1467 			"  <body%s>\n"
1468 			"%s\n"
1469 			"  </body>\n"
1470 			"</html>\n";
1471 
1472 	html = apr_psprintf(r->pool, html,
1473 			title ? oidc_util_html_escape(r->pool, title) : "",
1474 					html_head ? html_head : "",
1475 							on_load ? apr_psprintf(r->pool, " onload=\"%s()\"", on_load) : "",
1476 									html_body ? html_body : "<p></p>");
1477 
1478 	return oidc_util_http_send(r, html, strlen(html),
1479 			OIDC_CONTENT_TYPE_TEXT_HTML, status_code);
1480 }
1481 
1482 static char *html_error_template_contents = NULL;
1483 
1484 /*
1485  * get the full path to a file based on an (already) absolute filename or a filename
1486  * that is relative to the Apache root directory
1487  */
oidc_util_get_full_path(apr_pool_t * pool,const char * abs_or_rel_filename)1488 char* oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename) {
1489 	return (abs_or_rel_filename) ?
1490 			ap_server_root_relative(pool, abs_or_rel_filename) : NULL;
1491 }
1492 
1493 /*
1494  * send a user-facing error to the browser
1495  */
oidc_util_html_send_error(request_rec * r,const char * html_template,const char * error,const char * description,int status_code)1496 int oidc_util_html_send_error(request_rec *r, const char *html_template,
1497 		const char *error, const char *description, int status_code) {
1498 
1499 	char *html = "";
1500 
1501 	if (html_template != NULL) {
1502 
1503 		html_template = oidc_util_get_full_path(r->pool, html_template);
1504 
1505 		if (html_error_template_contents == NULL) {
1506 			int rc = oidc_util_file_read(r, html_template,
1507 					r->server->process->pool, &html_error_template_contents);
1508 			if (rc == FALSE) {
1509 				oidc_error(r, "could not read HTML error template: %s",
1510 						html_template);
1511 				html_error_template_contents = NULL;
1512 			}
1513 		}
1514 
1515 		if (html_error_template_contents) {
1516 			html = apr_psprintf(r->pool, html_error_template_contents,
1517 					oidc_util_html_escape(r->pool, error ? error : ""),
1518 					oidc_util_html_escape(r->pool,
1519 							description ? description : ""));
1520 
1521 			return oidc_util_http_send(r, html, strlen(html),
1522 					OIDC_CONTENT_TYPE_TEXT_HTML, status_code);
1523 		}
1524 	}
1525 
1526 	if (error != NULL) {
1527 		html = apr_psprintf(r->pool, "%s<p>Error: <pre>%s</pre></p>", html,
1528 				oidc_util_html_escape(r->pool, error));
1529 	}
1530 	if (description != NULL) {
1531 		html = apr_psprintf(r->pool, "%s<p>Description: <pre>%s</pre></p>",
1532 				html, oidc_util_html_escape(r->pool, description));
1533 	}
1534 
1535 	return oidc_util_html_send(r, "Error", NULL, NULL, html, status_code);
1536 }
1537 
1538 /*
1539  * read all bytes from the HTTP request
1540  */
oidc_util_read(request_rec * r,char ** rbuf)1541 static apr_byte_t oidc_util_read(request_rec *r, char **rbuf) {
1542 	apr_size_t bytes_read;
1543 	apr_size_t bytes_left;
1544 	apr_size_t len;
1545 	long read_length;
1546 
1547 	if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK) != OK)
1548 		return FALSE;
1549 
1550 	len = ap_should_client_block(r) ? r->remaining : 0;
1551 
1552 	if (len > OIDC_MAX_POST_DATA_LEN) {
1553 		oidc_error(r, "POST parameter value is too large: %lu bytes (max=%d)",
1554 				(unsigned long ) len, OIDC_MAX_POST_DATA_LEN);
1555 		return FALSE;
1556 	}
1557 
1558 	*rbuf = (char*) apr_palloc(r->pool, len + 1);
1559 	if (*rbuf == NULL) {
1560 		oidc_error(r, "could not allocate memory for %lu bytes of POST data.",
1561 				(unsigned long )len);
1562 		return FALSE;
1563 	}
1564 	(*rbuf)[len] = '\0';
1565 
1566 	bytes_read = 0;
1567 	bytes_left = len;
1568 	while (bytes_left > 0) {
1569 		read_length = ap_get_client_block(r, &(*rbuf)[bytes_read], bytes_left);
1570 		if (read_length == 0) {
1571 			(*rbuf)[bytes_read] = '\0';
1572 			break;
1573 		} else if (read_length < 0) {
1574 			oidc_error(r, "failed to read POST data from client");
1575 			return FALSE;
1576 		}
1577 		bytes_read += read_length;
1578 		bytes_left -= read_length;
1579 	}
1580 
1581 	return TRUE;
1582 }
1583 
1584 /*
1585  * read form-encoded parameters from a string in to a table
1586  */
oidc_util_read_form_encoded_params(request_rec * r,apr_table_t * table,char * data)1587 apr_byte_t oidc_util_read_form_encoded_params(request_rec *r,
1588 		apr_table_t *table, char *data) {
1589 	const char *key, *val, *p = data;
1590 
1591 	while (p && *p && (val = ap_getword(r->pool, &p, OIDC_CHAR_AMP))) {
1592 		key = ap_getword(r->pool, &val, OIDC_CHAR_EQUAL);
1593 		key = oidc_util_unescape_string(r, key);
1594 		val = oidc_util_unescape_string(r, val);
1595 		oidc_debug(r, "read: %s=%s", key, val);
1596 		apr_table_set(table, key, val);
1597 	}
1598 
1599 	oidc_debug(r, "parsed: %d bytes into %d elements",
1600 			data ? (int )strlen(data) : 0, apr_table_elts(table)->nelts);
1601 
1602 	return TRUE;
1603 }
1604 
oidc_userdata_set_post_param(request_rec * r,const char * post_param_name,const char * post_param_value)1605 static void oidc_userdata_set_post_param(request_rec *r,
1606 		const char *post_param_name, const char *post_param_value) {
1607 	apr_table_t *userdata_post_params = NULL;
1608 	apr_pool_userdata_get((void**) &userdata_post_params,
1609 			OIDC_USERDATA_POST_PARAMS_KEY, r->pool);
1610 	if (userdata_post_params == NULL)
1611 		userdata_post_params = apr_table_make(r->pool, 1);
1612 	apr_table_set(userdata_post_params, post_param_name, post_param_value);
1613 	apr_pool_userdata_set(userdata_post_params, OIDC_USERDATA_POST_PARAMS_KEY,
1614 			NULL, r->pool);
1615 
1616 }
1617 
1618 /*
1619  * read the POST parameters in to a table
1620  */
oidc_util_read_post_params(request_rec * r,apr_table_t * table,apr_byte_t propagate,const char * strip_param_name)1621 apr_byte_t oidc_util_read_post_params(request_rec *r, apr_table_t *table,
1622 		apr_byte_t propagate, const char *strip_param_name) {
1623 	apr_byte_t rc = FALSE;
1624 	char *data = NULL;
1625 	const apr_array_header_t *arr = NULL;
1626 	const apr_table_entry_t *elts = NULL;
1627 	int i = 0;
1628 	const char *content_type = NULL;
1629 
1630 	content_type = oidc_util_hdr_in_content_type_get(r);
1631 	if ((r->method_number != M_POST) || (strstr(content_type,
1632 			OIDC_CONTENT_TYPE_FORM_ENCODED) != content_type)) {
1633 		oidc_debug(r, "required content-type %s not found",
1634 				OIDC_CONTENT_TYPE_FORM_ENCODED);
1635 		goto end;
1636 	}
1637 
1638 	if (oidc_util_read(r, &data) != TRUE)
1639 		goto end;
1640 
1641 	rc = oidc_util_read_form_encoded_params(r, table, data);
1642 	if (rc != TRUE)
1643 		goto end;
1644 
1645 	if (propagate == FALSE)
1646 		goto end;
1647 
1648 	arr = apr_table_elts(table);
1649 	elts = (const apr_table_entry_t*) arr->elts;
1650 	for (i = 0; i < arr->nelts; i++)
1651 		if (apr_strnatcmp(elts[i].key, strip_param_name) != 0)
1652 			oidc_userdata_set_post_param(r, elts[i].key, elts[i].val);
1653 
1654 	end:
1655 
1656 	return rc;
1657 }
1658 
1659 /*
1660  * read a file from a path on disk
1661  */
oidc_util_file_read(request_rec * r,const char * path,apr_pool_t * pool,char ** result)1662 apr_byte_t oidc_util_file_read(request_rec *r, const char *path,
1663 		apr_pool_t *pool, char **result) {
1664 	apr_file_t *fd = NULL;
1665 	apr_status_t rc = APR_SUCCESS;
1666 	char s_err[128];
1667 	apr_finfo_t finfo;
1668 
1669 	/* open the file if it exists */
1670 	if ((rc = apr_file_open(&fd, path, APR_FOPEN_READ | APR_FOPEN_BUFFERED,
1671 			APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
1672 		oidc_warn(r, "no file found at: \"%s\"", path);
1673 		return FALSE;
1674 	}
1675 
1676 	/* the file exists, now lock it */
1677 	apr_file_lock(fd, APR_FLOCK_EXCLUSIVE);
1678 
1679 	/* move the read pointer to the very start of the cache file */
1680 	apr_off_t begin = 0;
1681 	apr_file_seek(fd, APR_SET, &begin);
1682 
1683 	/* get the file info so we know its size */
1684 	if ((rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, fd)) != APR_SUCCESS) {
1685 		oidc_error(r, "error calling apr_file_info_get on file: \"%s\" (%s)",
1686 				path, apr_strerror(rc, s_err, sizeof(s_err)));
1687 		goto error_close;
1688 	}
1689 
1690 	/* now that we have the size of the file, allocate a buffer that can contain its contents */
1691 	*result = apr_palloc(pool, finfo.size + 1);
1692 
1693 	/* read the file in to the buffer */
1694 	apr_size_t bytes_read = 0;
1695 	if ((rc = apr_file_read_full(fd, *result, finfo.size, &bytes_read))
1696 			!= APR_SUCCESS) {
1697 		oidc_error(r, "apr_file_read_full on (%s) returned an error: %s", path,
1698 				apr_strerror(rc, s_err, sizeof(s_err)));
1699 		goto error_close;
1700 	}
1701 
1702 	/* just to be sure, we set a \0 (we allocated space for it anyway) */
1703 	(*result)[bytes_read] = '\0';
1704 
1705 	/* check that we've got all of it */
1706 	if (bytes_read != finfo.size) {
1707 		oidc_error(r,
1708 				"apr_file_read_full on (%s) returned less bytes (%" APR_SIZE_T_FMT ") than expected: (%" APR_OFF_T_FMT ")",
1709 				path, bytes_read, finfo.size);
1710 		goto error_close;
1711 	}
1712 
1713 	/* we're done, unlock and close the file */
1714 	apr_file_unlock(fd);
1715 	apr_file_close(fd);
1716 
1717 	/* log successful content retrieval */
1718 	oidc_debug(r, "file read successfully \"%s\"", path);
1719 
1720 	return TRUE;
1721 
1722 error_close:
1723 
1724 	apr_file_unlock(fd);
1725 	apr_file_close(fd);
1726 
1727 	oidc_error(r, "return error");
1728 
1729 	return FALSE;
1730 }
1731 
1732 /*
1733  * write data to a file
1734  */
oidc_util_file_write(request_rec * r,const char * path,const char * data)1735 apr_byte_t oidc_util_file_write(request_rec *r, const char *path,
1736 		const char *data) {
1737 
1738 	apr_file_t *fd = NULL;
1739 	apr_status_t rc = APR_SUCCESS;
1740 	apr_size_t bytes_written = 0;
1741 	char s_err[128];
1742 
1743 	/* try to open the metadata file for writing, creating it if it does not exist */
1744 	if ((rc = apr_file_open(&fd, path,
1745 			(APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE),
1746 			APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
1747 		oidc_error(r, "file \"%s\" could not be opened (%s)", path,
1748 				apr_strerror(rc, s_err, sizeof(s_err)));
1749 		return FALSE;
1750 	}
1751 
1752 	/* lock the file and move the write pointer to the start of it */
1753 	apr_file_lock(fd, APR_FLOCK_EXCLUSIVE);
1754 	apr_off_t begin = 0;
1755 	apr_file_seek(fd, APR_SET, &begin);
1756 
1757 	/* calculate the length of the data, which is a string length */
1758 	apr_size_t len = strlen(data);
1759 
1760 	/* (blocking) write the number of bytes in the buffer */
1761 	rc = apr_file_write_full(fd, data, len, &bytes_written);
1762 
1763 	/* check for a system error */
1764 	if (rc != APR_SUCCESS) {
1765 		oidc_error(r, "could not write to: \"%s\" (%s)", path,
1766 				apr_strerror(rc, s_err, sizeof(s_err)));
1767 		return FALSE;
1768 	}
1769 
1770 	/* check that all bytes from the header were written */
1771 	if (bytes_written != len) {
1772 		oidc_error(r,
1773 				"could not write enough bytes to: \"%s\", bytes_written (%" APR_SIZE_T_FMT ") != len (%" APR_SIZE_T_FMT ")",
1774 				path, bytes_written, len);
1775 		return FALSE;
1776 	}
1777 
1778 	/* unlock and close the written file */
1779 	apr_file_unlock(fd);
1780 	apr_file_close(fd);
1781 
1782 	oidc_debug(r, "file \"%s\" written; number of bytes (%" APR_SIZE_T_FMT ")",
1783 			path, len);
1784 
1785 	return TRUE;
1786 }
1787 
1788 /*
1789  * see if two provided issuer identifiers match (cq. ignore trailing slash)
1790  */
oidc_util_issuer_match(const char * a,const char * b)1791 apr_byte_t oidc_util_issuer_match(const char *a, const char *b) {
1792 
1793 	/* check the "issuer" value against the one configure for the provider we got this id_token from */
1794 	if (apr_strnatcmp(a, b) != 0) {
1795 
1796 		/* no strict match, but we are going to accept if the difference is only a trailing slash */
1797 		int n1 = strlen(a);
1798 		int n2 = strlen(b);
1799 		int n = ((n1 == n2 + 1) && (a[n1 - 1] == OIDC_CHAR_FORWARD_SLASH)) ?
1800 				n2 :
1801 				(((n2 == n1 + 1) && (b[n2 - 1] == OIDC_CHAR_FORWARD_SLASH)) ?
1802 						n1 : 0);
1803 		if ((n == 0) || (strncmp(a, b, n) != 0))
1804 			return FALSE;
1805 	}
1806 
1807 	return TRUE;
1808 }
1809 
1810 /*
1811  * see if a certain string value is part of a JSON array with string elements
1812  */
oidc_util_json_array_has_value(request_rec * r,json_t * haystack,const char * needle)1813 apr_byte_t oidc_util_json_array_has_value(request_rec *r, json_t *haystack,
1814 		const char *needle) {
1815 
1816 	if ((haystack == NULL) || (!json_is_array(haystack)))
1817 		return FALSE;
1818 
1819 	int i;
1820 	for (i = 0; i < json_array_size(haystack); i++) {
1821 		json_t *elem = json_array_get(haystack, i);
1822 		if (!json_is_string(elem)) {
1823 			oidc_error(r, "unhandled in-array JSON non-string object type [%d]",
1824 					elem->type);
1825 			continue;
1826 		}
1827 		if (apr_strnatcmp(json_string_value(elem), needle) == 0) {
1828 			break;
1829 		}
1830 	}
1831 
1832 	//	oidc_debug(r,
1833 	//			"returning (%d=%d)", i,
1834 	//			haystack->value.array->nelts);
1835 
1836 	return (i == json_array_size(haystack)) ? FALSE : TRUE;
1837 }
1838 
1839 /*
1840  * set a HTTP header and/or environment variable to pass information to the application
1841  */
oidc_util_set_app_info(request_rec * r,const char * s_key,const char * s_value,const char * claim_prefix,apr_byte_t as_header,apr_byte_t as_env_var,apr_byte_t base64url)1842 void oidc_util_set_app_info(request_rec *r, const char *s_key,
1843 		const char *s_value, const char *claim_prefix, apr_byte_t as_header,
1844 		apr_byte_t as_env_var, apr_byte_t base64url) {
1845 
1846 	/* construct the header name, cq. put the prefix in front of a normalized key name */
1847 	const char *s_name = apr_psprintf(r->pool, "%s%s", claim_prefix,
1848 			oidc_normalize_header_name(r, s_key));
1849 	char *d_value = NULL;
1850 
1851 	if (as_header) {
1852 		if ((base64url == TRUE) && (s_value != NULL)) {
1853 			oidc_base64url_encode(r, &d_value, s_value, strlen(s_value), TRUE);
1854 		}
1855 		oidc_util_hdr_in_set(r, s_name, (d_value != NULL) ? d_value : s_value);
1856 	}
1857 
1858 	if (as_env_var) {
1859 
1860 		/* do some logging about this event */
1861 		oidc_debug(r, "setting environment variable \"%s: %s\"", s_name,
1862 				s_value);
1863 
1864 		apr_table_set(r->subprocess_env, s_name, s_value);
1865 	}
1866 }
1867 
1868 /*
1869  * set the user/claims information from the session in HTTP headers passed on to the application
1870  */
oidc_util_set_app_infos(request_rec * r,const json_t * j_attrs,const char * claim_prefix,const char * claim_delimiter,apr_byte_t as_header,apr_byte_t as_env_var,apr_byte_t base64url)1871 void oidc_util_set_app_infos(request_rec *r, const json_t *j_attrs,
1872 		const char *claim_prefix, const char *claim_delimiter,
1873 		apr_byte_t as_header, apr_byte_t as_env_var, apr_byte_t base64url) {
1874 
1875 	char s_int[255];
1876 	json_t *j_value = NULL;
1877 	const char *s_key = NULL;
1878 
1879 	/* if not attributes are set, nothing needs to be done */
1880 	if (j_attrs == NULL) {
1881 		oidc_debug(r, "no attributes to set");
1882 		return;
1883 	}
1884 
1885 	/* loop over the claims in the JSON structure */
1886 	void *iter = json_object_iter((json_t*) j_attrs);
1887 	while (iter) {
1888 
1889 		/* get the next key/value entry */
1890 		s_key = json_object_iter_key(iter);
1891 		j_value = json_object_iter_value(iter);
1892 
1893 		//		char *s_value= json_dumps(j_value, JSON_ENCODE_ANY);
1894 		//		oidc_util_set_app_info(r, s_key, s_value, claim_prefix);
1895 		//		free(s_value);
1896 
1897 		/* check if it is a single value string */
1898 		if (json_is_string(j_value)) {
1899 
1900 			/* set the single string in the application header whose name is based on the key and the prefix */
1901 			oidc_util_set_app_info(r, s_key, json_string_value(j_value),
1902 					claim_prefix, as_header, as_env_var, base64url);
1903 
1904 		} else if (json_is_boolean(j_value)) {
1905 
1906 			/* set boolean value in the application header whose name is based on the key and the prefix */
1907 			oidc_util_set_app_info(r, s_key,
1908 					(json_is_true(j_value) ? "1" : "0"), claim_prefix,
1909 					as_header, as_env_var, base64url);
1910 
1911 		} else if (json_is_integer(j_value)) {
1912 
1913 			if (sprintf(s_int, "%ld", (long) json_integer_value(j_value)) > 0) {
1914 				/* set long value in the application header whose name is based on the key and the prefix */
1915 				oidc_util_set_app_info(r, s_key, s_int, claim_prefix, as_header,
1916 						as_env_var, base64url);
1917 			} else {
1918 				oidc_warn(r,
1919 						"could not convert JSON number to string (> 255 characters?), skipping");
1920 			}
1921 
1922 		} else if (json_is_real(j_value)) {
1923 
1924 			/* set float value in the application header whose name is based on the key and the prefix */
1925 			oidc_util_set_app_info(r, s_key,
1926 					apr_psprintf(r->pool, "%lf", json_real_value(j_value)),
1927 					claim_prefix, as_header, as_env_var, base64url);
1928 
1929 		} else if (json_is_object(j_value)) {
1930 
1931 			/* set json value in the application header whose name is based on the key and the prefix */
1932 			oidc_util_set_app_info(r, s_key,
1933 					oidc_util_encode_json_object(r, j_value, 0), claim_prefix,
1934 					as_header, as_env_var, base64url);
1935 
1936 			/* check if it is a multi-value string */
1937 		} else if (json_is_array(j_value)) {
1938 
1939 			/* some logging about what we're going to do */
1940 			oidc_debug(r,
1941 					"parsing attribute array for key \"%s\" (#nr-of-elems: %llu)",
1942 					s_key, (unsigned long long )json_array_size(j_value));
1943 
1944 			/* string to hold the concatenated array string values */
1945 			char *s_concat = apr_pstrdup(r->pool, "");
1946 			size_t i = 0;
1947 
1948 			/* loop over the array */
1949 			for (i = 0; i < json_array_size(j_value); i++) {
1950 
1951 				/* get the current element */
1952 				json_t *elem = json_array_get(j_value, i);
1953 
1954 				/* check if it is a string */
1955 				if (json_is_string(elem)) {
1956 
1957 					/* concatenate the string to the s_concat value using the configured separator char */
1958 					// TODO: escape the delimiter in the values (maybe reuse/extract url-formatted code from oidc_session_identity_encode)
1959 					if (apr_strnatcmp(s_concat, "") != 0) {
1960 						s_concat = apr_psprintf(r->pool, "%s%s%s", s_concat,
1961 								claim_delimiter, json_string_value(elem));
1962 					} else {
1963 						s_concat = apr_psprintf(r->pool, "%s",
1964 								json_string_value(elem));
1965 					}
1966 
1967 				} else if (json_is_boolean(elem)) {
1968 
1969 					if (apr_strnatcmp(s_concat, "") != 0) {
1970 						s_concat = apr_psprintf(r->pool, "%s%s%s", s_concat,
1971 								claim_delimiter,
1972 								json_is_true(elem) ? "1" : "0");
1973 					} else {
1974 						s_concat = apr_psprintf(r->pool, "%s",
1975 								json_is_true(elem) ? "1" : "0");
1976 					}
1977 
1978 				} else {
1979 
1980 					/* don't know how to handle a non-string array element */
1981 					oidc_warn(r,
1982 							"unhandled in-array JSON object type [%d] for key \"%s\" when parsing claims array elements",
1983 							elem->type, s_key);
1984 				}
1985 			}
1986 
1987 			/* set the concatenated string */
1988 			oidc_util_set_app_info(r, s_key, s_concat, claim_prefix, as_header,
1989 					as_env_var, base64url);
1990 
1991 		} else {
1992 
1993 			/* no string and no array, so unclear how to handle this */
1994 			oidc_warn(r,
1995 					"unhandled JSON object type [%d] for key \"%s\" when parsing claims",
1996 					j_value->type, s_key);
1997 		}
1998 
1999 		iter = json_object_iter_next((json_t*) j_attrs, iter);
2000 	}
2001 }
2002 
2003 /*
2004  * parse a space separated string in to a hash table
2005  */
oidc_util_spaced_string_to_hashtable(apr_pool_t * pool,const char * str)2006 apr_hash_t* oidc_util_spaced_string_to_hashtable(apr_pool_t *pool,
2007 		const char *str) {
2008 	char *val;
2009 	const char *data = apr_pstrdup(pool, str);
2010 	apr_hash_t *result = apr_hash_make(pool);
2011 	while (*data && (val = ap_getword_white(pool, &data))) {
2012 		apr_hash_set(result, val, APR_HASH_KEY_STRING, val);
2013 	}
2014 	return result;
2015 }
2016 
2017 /*
2018  * compare two space separated value types
2019  */
oidc_util_spaced_string_equals(apr_pool_t * pool,const char * a,const char * b)2020 apr_byte_t oidc_util_spaced_string_equals(apr_pool_t *pool, const char *a,
2021 		const char *b) {
2022 
2023 	/* parse both entries as hash tables */
2024 	apr_hash_t *ht_a = oidc_util_spaced_string_to_hashtable(pool, a);
2025 	apr_hash_t *ht_b = oidc_util_spaced_string_to_hashtable(pool, b);
2026 
2027 	/* first compare the length of both response_types */
2028 	if (apr_hash_count(ht_a) != apr_hash_count(ht_b))
2029 		return FALSE;
2030 
2031 	/* then loop over all entries */
2032 	apr_hash_index_t *hi;
2033 	for (hi = apr_hash_first(NULL, ht_a); hi; hi = apr_hash_next(hi)) {
2034 		const char *k;
2035 		const char *v;
2036 		apr_hash_this(hi, (const void**) &k, NULL, (void**) &v);
2037 		if (apr_hash_get(ht_b, k, APR_HASH_KEY_STRING) == NULL)
2038 			return FALSE;
2039 	}
2040 
2041 	/* if we've made it this far, a an b are equal in length and every element in a is in b */
2042 	return TRUE;
2043 }
2044 
2045 /*
2046  * see if a particular value is part of a space separated value
2047  */
oidc_util_spaced_string_contains(apr_pool_t * pool,const char * str,const char * match)2048 apr_byte_t oidc_util_spaced_string_contains(apr_pool_t *pool, const char *str,
2049 		const char *match) {
2050 	apr_hash_t *ht = oidc_util_spaced_string_to_hashtable(pool, str);
2051 	return (apr_hash_get(ht, match, APR_HASH_KEY_STRING) != NULL);
2052 }
2053 
2054 /*
2055  * get (optional) string from a JSON object
2056  */
oidc_json_object_get_string(apr_pool_t * pool,json_t * json,const char * name,char ** value,const char * default_value)2057 apr_byte_t oidc_json_object_get_string(apr_pool_t *pool, json_t *json,
2058 		const char *name, char **value, const char *default_value) {
2059 	*value = default_value ? apr_pstrdup(pool, default_value) : NULL;
2060 	if (json != NULL) {
2061 		json_t *v = json_object_get(json, name);
2062 		if ((v != NULL) && (json_is_string(v))) {
2063 			*value = apr_pstrdup(pool, json_string_value(v));
2064 		}
2065 	}
2066 	return TRUE;
2067 }
2068 
2069 /*
2070  * get (optional) int from a JSON object
2071  */
oidc_json_object_get_int(apr_pool_t * pool,json_t * json,const char * name,int * value,const int default_value)2072 apr_byte_t oidc_json_object_get_int(apr_pool_t *pool, json_t *json,
2073 		const char *name, int *value, const int default_value) {
2074 	*value = default_value;
2075 	if (json != NULL) {
2076 		json_t *v = json_object_get(json, name);
2077 		if ((v != NULL) && (json_is_integer(v))) {
2078 			*value = json_integer_value(v);
2079 		}
2080 	}
2081 	return TRUE;
2082 }
2083 
2084 /*
2085  * get (optional) boolean from a JSON object
2086  */
oidc_json_object_get_bool(apr_pool_t * pool,json_t * json,const char * name,int * value,const int default_value)2087 apr_byte_t oidc_json_object_get_bool(apr_pool_t *pool, json_t *json,
2088 		const char *name, int *value, const int default_value) {
2089 	*value = default_value;
2090 	if (json != NULL) {
2091 		json_t *v = json_object_get(json, name);
2092 		if ((v != NULL) && (json_is_boolean(v))) {
2093 			*value = json_is_true(v);
2094 			return TRUE;
2095 		}
2096 	}
2097 	return FALSE;
2098 }
2099 
2100 /*
2101  * merge two JSON objects
2102  */
oidc_util_json_merge(request_rec * r,json_t * src,json_t * dst)2103 apr_byte_t oidc_util_json_merge(request_rec *r, json_t *src, json_t *dst) {
2104 
2105 	const char *key;
2106 	json_t *value = NULL;
2107 	void *iter = NULL;
2108 
2109 	if ((src == NULL) || (dst == NULL))
2110 		return FALSE;
2111 
2112 	oidc_debug(r, "src=%s, dst=%s",
2113 			oidc_util_encode_json_object(r, src, JSON_COMPACT),
2114 			oidc_util_encode_json_object(r, dst, JSON_COMPACT));
2115 
2116 	iter = json_object_iter(src);
2117 	while (iter) {
2118 		key = json_object_iter_key(iter);
2119 		value = json_object_iter_value(iter);
2120 		json_object_set(dst, key, value);
2121 		iter = json_object_iter_next(src, iter);
2122 	}
2123 
2124 	oidc_debug(r, "result dst=%s",
2125 			oidc_util_encode_json_object(r, dst, JSON_COMPACT));
2126 
2127 	return TRUE;
2128 }
2129 
2130 /*
2131  * add query encoded parameters to a table
2132  */
oidc_util_table_add_query_encoded_params(apr_pool_t * pool,apr_table_t * table,const char * params)2133 void oidc_util_table_add_query_encoded_params(apr_pool_t *pool,
2134 		apr_table_t *table, const char *params) {
2135 	if (params != NULL) {
2136 		const char *key, *val;
2137 		const char *p = params;
2138 		while (*p && (val = ap_getword(pool, &p, OIDC_CHAR_AMP))) {
2139 			key = ap_getword(pool, &val, OIDC_CHAR_EQUAL);
2140 			ap_unescape_url((char*) key);
2141 			ap_unescape_url((char*) val);
2142 			apr_table_add(table, key, val);
2143 		}
2144 	}
2145 }
2146 
2147 /*
2148  * create a symmetric key from a client_secret
2149  */
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)2150 apr_byte_t oidc_util_create_symmetric_key(request_rec *r,
2151 		const char *client_secret, unsigned int r_key_len,
2152 		const char *hash_algo, apr_byte_t set_kid, oidc_jwk_t **jwk) {
2153 	oidc_jose_error_t err;
2154 	unsigned char *key = NULL;
2155 	unsigned int key_len;
2156 
2157 	if ((client_secret != NULL) && (strlen(client_secret) > 0)) {
2158 
2159 		if (hash_algo == NULL) {
2160 			key = (unsigned char*) client_secret;
2161 			key_len = strlen(client_secret);
2162 		} else {
2163 			/* hash the client_secret first, this is OpenID Connect specific */
2164 			oidc_jose_hash_bytes(r->pool, hash_algo,
2165 					(const unsigned char*) client_secret, strlen(client_secret),
2166 					&key, &key_len, &err);
2167 		}
2168 
2169 		if ((key != NULL) && (key_len > 0)) {
2170 			if ((r_key_len != 0) && (key_len >= r_key_len))
2171 				key_len = r_key_len;
2172 			oidc_debug(r, "key_len=%d", key_len);
2173 			*jwk = oidc_jwk_create_symmetric_key(r->pool, NULL, key, key_len,
2174 					set_kid, &err);
2175 		}
2176 
2177 		if (*jwk == NULL) {
2178 			oidc_error(r, "could not create JWK from the provided secret: %s",
2179 					oidc_jose_e2s(r->pool, err));
2180 			return FALSE;
2181 		}
2182 	}
2183 
2184 	return TRUE;
2185 }
2186 
2187 /*
2188  * merge provided keys and client secret in to a single hashtable
2189  */
oidc_util_merge_symmetric_key(apr_pool_t * pool,const apr_array_header_t * keys,oidc_jwk_t * jwk)2190 apr_hash_t* oidc_util_merge_symmetric_key(apr_pool_t *pool,
2191 		const apr_array_header_t *keys, oidc_jwk_t *jwk) {
2192 	apr_hash_t *result = apr_hash_make(pool);
2193 	int i = 0;
2194 	if (keys != NULL) {
2195 		for (i = 0; i < keys->nelts; i++) {
2196 			const oidc_jwk_t *elem = ((const oidc_jwk_t**) keys->elts)[i];
2197 			apr_hash_set(result, elem->kid, APR_HASH_KEY_STRING, elem);
2198 		}
2199 	}
2200 	if (jwk != NULL) {
2201 		apr_hash_set(result, jwk->kid, APR_HASH_KEY_STRING, jwk);
2202 	}
2203 	return result;
2204 }
2205 
2206 /*
2207  * openssl hash and base64 encode
2208  */
oidc_util_hash_string_and_base64url_encode(request_rec * r,const char * openssl_hash_algo,const char * input,char ** output)2209 apr_byte_t oidc_util_hash_string_and_base64url_encode(request_rec *r,
2210 		const char *openssl_hash_algo, const char *input, char **output) {
2211 	oidc_jose_error_t err;
2212 	unsigned char *hashed = NULL;
2213 	unsigned int hashed_len = 0;
2214 	if (oidc_jose_hash_bytes(r->pool, openssl_hash_algo,
2215 			(const unsigned char*) input, strlen(input), &hashed, &hashed_len,
2216 			&err) == FALSE) {
2217 		oidc_error(r, "oidc_jose_hash_bytes returned an error: %s", err.text);
2218 		return FALSE;
2219 	}
2220 
2221 	if (oidc_base64url_encode(r, output, (const char*) hashed, hashed_len, TRUE)
2222 			<= 0) {
2223 		oidc_error(r, "oidc_base64url_encode returned an error: %s", err.text);
2224 		return FALSE;
2225 	}
2226 	return TRUE;
2227 }
2228 
2229 /*
2230  * merge two key sets
2231  */
oidc_util_merge_key_sets(apr_pool_t * pool,apr_hash_t * k1,const apr_array_header_t * k2)2232 apr_hash_t* oidc_util_merge_key_sets(apr_pool_t *pool, apr_hash_t *k1,
2233 		const apr_array_header_t *k2) {
2234 	apr_hash_t *rv = k1 ? apr_hash_copy(pool, k1) : apr_hash_make(pool);
2235 	int i = 0;
2236 	if (k2 != NULL) {
2237 		for (i = 0; i < k2->nelts; i++) {
2238 			const oidc_jwk_t *jwk = ((const oidc_jwk_t**) k2->elts)[i];
2239 			apr_hash_set(rv, jwk->kid, APR_HASH_KEY_STRING, jwk);
2240 		}
2241 	}
2242 	return rv;
2243 }
2244 
oidc_util_merge_key_sets_hash(apr_pool_t * pool,apr_hash_t * k1,apr_hash_t * k2)2245 apr_hash_t* oidc_util_merge_key_sets_hash(apr_pool_t *pool, apr_hash_t *k1,
2246 		apr_hash_t *k2) {
2247 	if (k1 == NULL) {
2248 		if (k2 == NULL)
2249 			return apr_hash_make(pool);
2250 		return k2;
2251 	}
2252 	if (k2 == NULL)
2253 		return k1;
2254 	return apr_hash_overlay(pool, k1, k2);
2255 }
2256 
2257 /*
2258  * regexp substitute
2259  *   Example:
2260  *     regex: "^.*([0-9]+).*$"
2261  *     replace: "$1"
2262  *     text_original: "match 292 numbers"
2263  *     text_replaced: "292"
2264  */
oidc_util_regexp_substitute(apr_pool_t * pool,const char * input,const char * regexp,const char * replace,char ** output,char ** error_str)2265 apr_byte_t oidc_util_regexp_substitute(apr_pool_t *pool, const char *input,
2266 		const char *regexp, const char *replace, char **output,
2267 		char **error_str) {
2268 
2269 	const char *errorptr = NULL;
2270 	int erroffset;
2271 	char *substituted = NULL;
2272 	apr_byte_t rc = FALSE;
2273 
2274 	pcre *preg = pcre_compile(regexp, 0, &errorptr, &erroffset, NULL);
2275 	if (preg == NULL) {
2276 		*error_str = apr_psprintf(pool,
2277 				"pattern [%s] is not a valid regular expression", regexp);
2278 		goto out;
2279 	}
2280 
2281 	if (strlen(input) >= OIDC_PCRE_MAXCAPTURE - 1) {
2282 		*error_str =
2283 				apr_psprintf(pool,
2284 						"string length (%d) is larger than the maximum allowed for pcre_subst (%d)",
2285 						(int) strlen(input), OIDC_PCRE_MAXCAPTURE - 1);
2286 		goto out;
2287 	}
2288 
2289 	substituted = pcre_subst(preg, NULL, input, (int) strlen(input), 0, 0,
2290 			replace);
2291 	if (substituted == NULL) {
2292 		*error_str =
2293 				apr_psprintf(pool,
2294 						"unknown error could not match string [%s] using pattern [%s] and replace matches in [%s]",
2295 						input, regexp, replace);
2296 		goto out;
2297 	}
2298 
2299 	*output = apr_pstrdup(pool, substituted);
2300 	rc = TRUE;
2301 
2302 	out: if (substituted)
2303 		pcre_free(substituted);
2304 	if (preg)
2305 		pcre_free(preg);
2306 
2307 	return rc;
2308 }
2309 
2310 /*
2311  * regexp match
2312  */
2313 #define OIDC_UTIL_REGEXP_MATCH_SIZE 30
2314 #define OIDC_UTIL_REGEXP_MATCH_NR 1
2315 
oidc_util_regexp_first_match(apr_pool_t * pool,const char * input,const char * regexp,char ** output,char ** error_str)2316 apr_byte_t oidc_util_regexp_first_match(apr_pool_t *pool, const char *input,
2317 		const char *regexp, char **output, char **error_str) {
2318 	const char *errorptr = NULL;
2319 	int erroffset;
2320 	int rc = 0;
2321 	int subStr[OIDC_UTIL_REGEXP_MATCH_SIZE];
2322 	const char *psubStrMatchStr = NULL;
2323 	apr_byte_t rv = FALSE;
2324 
2325 	pcre *preg = pcre_compile(regexp, 0, &errorptr, &erroffset, NULL);
2326 	if (preg == NULL) {
2327 		*error_str = apr_psprintf(pool,
2328 				"pattern [%s] is not a valid regular expression", regexp);
2329 		goto out;
2330 	}
2331 
2332 	if ((rc = pcre_exec(preg, NULL, input, (int) strlen(input), 0, 0, subStr,
2333 			OIDC_UTIL_REGEXP_MATCH_SIZE)) < 0) {
2334 		switch (rc) {
2335 		case PCRE_ERROR_NOMATCH:
2336 			*error_str = apr_pstrdup(pool, "string did not match the pattern");
2337 			break;
2338 		case PCRE_ERROR_NULL:
2339 			*error_str = apr_pstrdup(pool, "something was null");
2340 			break;
2341 		case PCRE_ERROR_BADOPTION:
2342 			*error_str = apr_pstrdup(pool, "a bad option was passed");
2343 			break;
2344 		case PCRE_ERROR_BADMAGIC:
2345 			*error_str = apr_pstrdup(pool,
2346 					"magic number bad (compiled re corrupt?)");
2347 			break;
2348 		case PCRE_ERROR_UNKNOWN_NODE:
2349 			*error_str = apr_pstrdup(pool,
2350 					"something kooky in the compiled re");
2351 			break;
2352 		case PCRE_ERROR_NOMEMORY:
2353 			*error_str = apr_pstrdup(pool, "ran out of memory");
2354 			break;
2355 		default:
2356 			*error_str = apr_psprintf(pool, "unknown error: %d", rc);
2357 			break;
2358 		}
2359 		goto out;
2360 	}
2361 
2362 	if (output) {
2363 
2364 		if (pcre_get_substring(input, subStr, rc, OIDC_UTIL_REGEXP_MATCH_NR,
2365 				&(psubStrMatchStr)) <= 0) {
2366 			*error_str = apr_psprintf(pool, "pcre_get_substring failed (rc=%d)",
2367 					rc);
2368 			goto out;
2369 		}
2370 
2371 		*output = apr_pstrdup(pool, psubStrMatchStr);
2372 	}
2373 
2374 	rv = TRUE;
2375 
2376 out:
2377 
2378 	if (psubStrMatchStr)
2379 		pcre_free_substring(psubStrMatchStr);
2380 	if (preg)
2381 		pcre_free(preg);
2382 
2383 	return rv;
2384 }
2385 
oidc_util_cookie_domain_valid(const char * hostname,char * cookie_domain)2386 int oidc_util_cookie_domain_valid(const char *hostname, char *cookie_domain) {
2387 	char *p = NULL;
2388 	char *check_cookie = cookie_domain;
2389 	// Skip past the first char of a cookie_domain that starts
2390 	// with a ".", ASCII 46
2391 	if (check_cookie[0] == 46) {
2392 		p = strstr(hostname, ++check_cookie);
2393 	} else {
2394 		p = strstr(hostname, check_cookie);
2395 	}
2396 	if ((p == NULL) || (apr_strnatcmp(check_cookie, p) != 0)) {
2397 		return FALSE;
2398 	}
2399 	return TRUE;
2400 }
2401 
oidc_util_hdr_in_get(const request_rec * r,const char * name)2402 static const char* oidc_util_hdr_in_get(const request_rec *r, const char *name) {
2403 	const char *value = apr_table_get(r->headers_in, name);
2404 	if (value)
2405 		oidc_debug(r, "%s=%s", name, value);
2406 	return value;
2407 }
2408 
oidc_util_hdr_in_get_left_most_only(const request_rec * r,const char * name,const char * separator)2409 static const char* oidc_util_hdr_in_get_left_most_only(const request_rec *r,
2410 		const char *name, const char *separator) {
2411 	char *last = NULL;
2412 	const char *value = oidc_util_hdr_in_get(r, name);
2413 	if (value)
2414 		return apr_strtok(apr_pstrdup(r->pool, value), separator, &last);
2415 	return NULL;
2416 }
2417 
oidc_util_hdr_in_contains(const request_rec * r,const char * name,const char * separator,const char postfix_separator,const char * needle)2418 static apr_byte_t oidc_util_hdr_in_contains(const request_rec *r,
2419 		const char *name, const char *separator, const char postfix_separator,
2420 		const char *needle) {
2421 	char *ctx = NULL, *elem = NULL;
2422 	const char *value = oidc_util_hdr_in_get(r, name);
2423 	apr_byte_t rc = FALSE;
2424 	if (value) {
2425 		elem = apr_strtok(apr_pstrdup(r->pool, value), separator, &ctx);
2426 		while (elem != NULL) {
2427 			while (*elem == OIDC_CHAR_SPACE)
2428 				elem++;
2429 			if ((strncmp(elem, needle, strlen(needle)) == 0)
2430 					&& ((elem[strlen(needle)] == '\0')
2431 							|| (elem[strlen(needle)] == postfix_separator))) {
2432 				rc = TRUE;
2433 				break;
2434 			}
2435 			elem = apr_strtok(NULL, separator, &ctx);
2436 		}
2437 	}
2438 	return rc;
2439 }
2440 
oidc_util_hdr_table_set(const request_rec * r,apr_table_t * table,const char * name,const char * value)2441 static void oidc_util_hdr_table_set(const request_rec *r, apr_table_t *table,
2442 		const char *name, const char *value) {
2443 
2444 	if (value != NULL) {
2445 
2446 		char *s_value = apr_pstrdup(r->pool, value);
2447 
2448 		/*
2449 		 * sanitize the header value by replacing line feeds with spaces
2450 		 * just like the Apache header input algorithms do for incoming headers
2451 		 *
2452 		 * this makes it impossible to have line feeds in values but that is
2453 		 * compliant with RFC 7230 (and impossible for regular headers due to Apache's
2454 		 * parsing of headers anyway) and fixes a security vulnerability on
2455 		 * overwriting/setting outgoing headers when used in proxy mode
2456 		 */
2457 		char *p = NULL;
2458 		while ((p = strchr(s_value, '\n')))
2459 			*p = OIDC_CHAR_SPACE;
2460 
2461 		oidc_debug(r, "%s: %s", name, s_value);
2462 		apr_table_set(table, name, s_value);
2463 
2464 	} else {
2465 
2466 		oidc_debug(r, "unset %s", name);
2467 		apr_table_unset(table, name);
2468 
2469 	}
2470 }
2471 
oidc_util_hdr_out_set(const request_rec * r,const char * name,const char * value)2472 static void oidc_util_hdr_out_set(const request_rec *r, const char *name,
2473 		const char *value) {
2474 	oidc_util_hdr_table_set(r, r->headers_out, name, value);
2475 }
2476 
oidc_util_hdr_out_get(const request_rec * r,const char * name)2477 static const char* oidc_util_hdr_out_get(const request_rec *r, const char *name) {
2478 	return apr_table_get(r->headers_out, name);
2479 }
2480 
oidc_util_hdr_err_out_add(const request_rec * r,const char * name,const char * value)2481 void oidc_util_hdr_err_out_add(const request_rec *r, const char *name,
2482 		const char *value) {
2483 	oidc_debug(r, "%s: %s", name, value);
2484 	apr_table_add(r->err_headers_out, name, value);
2485 }
2486 
oidc_util_hdr_in_set(const request_rec * r,const char * name,const char * value)2487 void oidc_util_hdr_in_set(const request_rec *r, const char *name,
2488 		const char *value) {
2489 	oidc_util_hdr_table_set(r, r->headers_in, name, value);
2490 }
2491 
oidc_util_hdr_in_cookie_get(const request_rec * r)2492 const char* oidc_util_hdr_in_cookie_get(const request_rec *r) {
2493 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_COOKIE);
2494 }
2495 
oidc_util_hdr_in_cookie_set(const request_rec * r,const char * value)2496 void oidc_util_hdr_in_cookie_set(const request_rec *r, const char *value) {
2497 	oidc_util_hdr_in_set(r, OIDC_HTTP_HDR_COOKIE, value);
2498 }
2499 
oidc_util_hdr_in_user_agent_get(const request_rec * r)2500 const char* oidc_util_hdr_in_user_agent_get(const request_rec *r) {
2501 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_USER_AGENT);
2502 }
2503 
oidc_util_hdr_in_x_forwarded_for_get(const request_rec * r)2504 const char* oidc_util_hdr_in_x_forwarded_for_get(const request_rec *r) {
2505 	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_FOR,
2506 			OIDC_STR_COMMA OIDC_STR_SPACE);
2507 }
2508 
oidc_util_hdr_in_content_type_get(const request_rec * r)2509 const char* oidc_util_hdr_in_content_type_get(const request_rec *r) {
2510 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_CONTENT_TYPE);
2511 }
2512 
oidc_util_hdr_in_content_length_get(const request_rec * r)2513 const char* oidc_util_hdr_in_content_length_get(const request_rec *r) {
2514 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_CONTENT_LENGTH);
2515 }
2516 
oidc_util_hdr_in_x_requested_with_get(const request_rec * r)2517 const char* oidc_util_hdr_in_x_requested_with_get(const request_rec *r) {
2518 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_X_REQUESTED_WITH);
2519 }
2520 
oidc_util_hdr_in_accept_get(const request_rec * r)2521 const char* oidc_util_hdr_in_accept_get(const request_rec *r) {
2522 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_ACCEPT);
2523 }
2524 
oidc_util_hdr_in_accept_contains(const request_rec * r,const char * needle)2525 apr_byte_t oidc_util_hdr_in_accept_contains(const request_rec *r,
2526 		const char *needle) {
2527 	return oidc_util_hdr_in_contains(r, OIDC_HTTP_HDR_ACCEPT, OIDC_STR_COMMA,
2528 			OIDC_CHAR_SEMI_COLON, needle);
2529 }
2530 
oidc_util_hdr_in_authorization_get(const request_rec * r)2531 const char* oidc_util_hdr_in_authorization_get(const request_rec *r) {
2532 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_AUTHORIZATION);
2533 }
2534 
oidc_util_hdr_in_x_forwarded_proto_get(const request_rec * r)2535 const char* oidc_util_hdr_in_x_forwarded_proto_get(const request_rec *r) {
2536 	return oidc_util_hdr_in_get_left_most_only(r,
2537 			OIDC_HTTP_HDR_X_FORWARDED_PROTO, OIDC_STR_COMMA OIDC_STR_SPACE);
2538 }
2539 
oidc_util_hdr_in_x_forwarded_port_get(const request_rec * r)2540 const char* oidc_util_hdr_in_x_forwarded_port_get(const request_rec *r) {
2541 	return oidc_util_hdr_in_get_left_most_only(r,
2542 			OIDC_HTTP_HDR_X_FORWARDED_PORT, OIDC_STR_COMMA OIDC_STR_SPACE);
2543 }
2544 
oidc_util_hdr_in_x_forwarded_host_get(const request_rec * r)2545 const char* oidc_util_hdr_in_x_forwarded_host_get(const request_rec *r) {
2546 	return oidc_util_hdr_in_get_left_most_only(r,
2547 			OIDC_HTTP_HDR_X_FORWARDED_HOST, OIDC_STR_COMMA OIDC_STR_SPACE);
2548 }
2549 
oidc_util_hdr_in_host_get(const request_rec * r)2550 const char* oidc_util_hdr_in_host_get(const request_rec *r) {
2551 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_HOST);
2552 }
2553 
oidc_util_hdr_out_location_set(const request_rec * r,const char * value)2554 void oidc_util_hdr_out_location_set(const request_rec *r, const char *value) {
2555 	oidc_util_hdr_out_set(r, OIDC_HTTP_HDR_LOCATION, value);
2556 }
2557 
oidc_util_hdr_out_location_get(const request_rec * r)2558 const char* oidc_util_hdr_out_location_get(const request_rec *r) {
2559 	return oidc_util_hdr_out_get(r, OIDC_HTTP_HDR_LOCATION);
2560 }
2561 
oidc_util_get_provided_token_binding_id(const request_rec * r)2562 const char* oidc_util_get_provided_token_binding_id(const request_rec *r) {
2563 	const char *result = NULL;
2564 	if (r->subprocess_env != NULL)
2565 		result = apr_table_get(r->subprocess_env, OIDC_TB_CFG_PROVIDED_ENV_VAR);
2566 	return result;
2567 }
2568 
oidc_util_get_client_cert_fingerprint(request_rec * r)2569 const char* oidc_util_get_client_cert_fingerprint(request_rec *r) {
2570 	const char *fingerprint = NULL;
2571 
2572 	if (r->subprocess_env == NULL)
2573 		goto end;
2574 
2575 	fingerprint = apr_table_get(r->subprocess_env,
2576 			OIDC_TB_CFG_FINGERPRINT_ENV_VAR);
2577 	if (fingerprint == NULL) {
2578 		oidc_debug(r, "no %s environment variable found",
2579 				OIDC_TB_CFG_FINGERPRINT_ENV_VAR);
2580 		goto end;
2581 	}
2582 
2583 end:
2584 
2585 	return fingerprint;
2586 }
2587 
oidc_util_json_validate_cnf_tbh(request_rec * r,int token_binding_policy,const char * tbh_str)2588 apr_byte_t oidc_util_json_validate_cnf_tbh(request_rec *r,
2589 		int token_binding_policy, const char *tbh_str) {
2590 	const char *tbp_str = NULL;
2591 	char *tbp = NULL;
2592 	int tbp_len = -1;
2593 	unsigned char *tbp_hash = NULL;
2594 	unsigned int tbp_hash_len = -1;
2595 	char *tbh = NULL;
2596 	int tbh_len = -1;
2597 
2598 	tbp_str = oidc_util_get_provided_token_binding_id(r);
2599 	if (tbp_str == NULL) {
2600 		oidc_debug(r,
2601 				"no Provided Token Binding ID environment variable found");
2602 		goto out_err;
2603 	}
2604 
2605 	tbp_len = oidc_base64url_decode(r->pool, &tbp, tbp_str);
2606 	if (tbp_len <= 0) {
2607 		oidc_warn(r,
2608 				"Provided Token Binding ID environment variable could not be decoded");
2609 		goto out_err;
2610 	}
2611 
2612 	if (oidc_jose_hash_bytes(r->pool, OIDC_JOSE_ALG_SHA256,
2613 			(const unsigned char*) tbp, tbp_len, &tbp_hash, &tbp_hash_len,
2614 			NULL) == FALSE) {
2615 		oidc_warn(r,
2616 				"hashing Provided Token Binding ID environment variable failed");
2617 		goto out_err;
2618 	}
2619 
2620 	tbh_len = oidc_base64url_decode(r->pool, &tbh, tbh_str);
2621 	if (tbh_len <= 0) {
2622 		oidc_warn(r, "cnf[\"tbh\"] provided but it could not be decoded");
2623 		goto out_err;
2624 	}
2625 
2626 	if (tbp_hash_len != tbh_len) {
2627 		oidc_warn(r,
2628 				"hash length of provided token binding ID environment variable: %d does not match length of cnf[\"tbh\"]: %d",
2629 				tbp_hash_len, tbh_len);
2630 		goto out_err;
2631 	}
2632 
2633 	if (memcmp(tbp_hash, tbh, tbh_len) != 0) {
2634 		oidc_warn(r,
2635 				"hash of provided token binding ID environment variable does not match cnf[\"tbh\"]");
2636 		goto out_err;
2637 	}
2638 
2639 	oidc_debug(r,
2640 			"hash of provided token binding ID environment variable matches cnf[\"tbh\"]");
2641 
2642 	return TRUE;
2643 
2644 out_err:
2645 
2646 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_OPTIONAL)
2647 		return TRUE;
2648 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_ENFORCED)
2649 		return FALSE;
2650 
2651 	// token_binding_policy == OIDC_TOKEN_BINDING_POLICY_REQURIED
2652 	return (tbp_str == NULL);
2653 }
2654 
oidc_util_json_validate_cnf_x5t_s256(request_rec * r,int token_binding_policy,const char * x5t_256_str)2655 apr_byte_t oidc_util_json_validate_cnf_x5t_s256(request_rec *r,
2656 		int token_binding_policy, const char *x5t_256_str) {
2657 	const char *fingerprint = NULL;
2658 
2659 	fingerprint = oidc_util_get_client_cert_fingerprint(r);
2660 	if (fingerprint == NULL) {
2661 		oidc_debug(r, "no certificate (fingerprint) provided");
2662 		goto out_err;
2663 	}
2664 
2665 	if (apr_strnatcmp(fingerprint, x5t_256_str) != 0) {
2666 		oidc_warn(r,
2667 				"fingerprint of provided cert (%s) does not match cnf[\"x5t#S256\"] (%s)",
2668 				fingerprint, x5t_256_str);
2669 		goto out_err;
2670 	}
2671 
2672 	oidc_debug(r, "fingerprint of provided cert (%s) matches cnf[\"x5t#S256\"]",
2673 			fingerprint);
2674 
2675 	return TRUE;
2676 
2677 	out_err:
2678 
2679 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_OPTIONAL)
2680 		return TRUE;
2681 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_ENFORCED)
2682 		return FALSE;
2683 
2684 	// token_binding_policy == OIDC_TOKEN_BINDING_POLICY_REQURIED
2685 	return (fingerprint == NULL);
2686 }
2687 
2688 /*
2689  * validate the "cnf" claim in a JWT payload
2690  */
oidc_util_json_validate_cnf(request_rec * r,json_t * jwt,int token_binding_policy)2691 apr_byte_t oidc_util_json_validate_cnf(request_rec *r, json_t *jwt,
2692 		int token_binding_policy) {
2693 	char *tbh_str = NULL;
2694 
2695 	oidc_debug(r, "enter: policy=%s",
2696 			oidc_token_binding_policy2str(r->pool, token_binding_policy));
2697 
2698 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_DISABLED)
2699 		return TRUE;
2700 
2701 	json_t *cnf = json_object_get(jwt, OIDC_CLAIM_CNF);
2702 	if (cnf == NULL) {
2703 		oidc_debug(r, "no \"%s\" claim found in the token", OIDC_CLAIM_CNF);
2704 		goto out_err;
2705 	}
2706 
2707 	oidc_jose_get_string(r->pool, cnf, OIDC_CLAIM_CNF_TBH, FALSE, &tbh_str,
2708 			NULL);
2709 	if (tbh_str != NULL)
2710 		return oidc_util_json_validate_cnf_tbh(r, token_binding_policy, tbh_str);
2711 
2712 	oidc_jose_get_string(r->pool, cnf, OIDC_CLAIM_CNF_X5T_S256, FALSE, &tbh_str,
2713 			NULL);
2714 	if (tbh_str != NULL)
2715 		return oidc_util_json_validate_cnf_x5t_s256(r, token_binding_policy,
2716 				tbh_str);
2717 
2718 	oidc_debug(r,
2719 			" \"%s\" claim found in the token but no \"%s\" or \"%s\" key found inside",
2720 			OIDC_CLAIM_CNF, OIDC_CLAIM_CNF_TBH, OIDC_CLAIM_CNF_X5T_S256);
2721 
2722 out_err:
2723 
2724 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_OPTIONAL)
2725 		return TRUE;
2726 	if (token_binding_policy == OIDC_TOKEN_BINDING_POLICY_ENFORCED)
2727 		return FALSE;
2728 
2729 	// token_binding_policy == OIDC_TOKEN_BINDING_POLICY_REQUIRED
2730 	// TODO: we don't know which token binding the client supports, do we ?
2731 	return FALSE;
2732 }
2733