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 <httpd.h>
45 #include <http_config.h>
46 #include <http_log.h>
47 #include <http_request.h>
48 
49 #include "mod_auth_openidc.h"
50 #include "parse.h"
51 
52 #include <openssl/opensslconf.h>
53 #include <openssl/opensslv.h>
54 
55 #ifdef USE_URANDOM
56 
57 #include <sys/stat.h>
58 #include <fcntl.h>
59 #include <unistd.h>
60 
61 #define DEV_RANDOM "/dev/urandom"
62 
63 #endif
64 
65 extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
66 
oidc_proto_generate_random_bytes(request_rec * r,unsigned char * buf,apr_size_t length)67 apr_status_t oidc_proto_generate_random_bytes(request_rec *r,
68 		unsigned char *buf, apr_size_t length) {
69 	apr_status_t rv;
70 
71 #ifndef USE_URANDOM
72 
73 	oidc_debug(r,
74 			"apr_generate_random_bytes call for %" APR_SIZE_T_FMT " bytes",
75 			length);
76 	rv = apr_generate_random_bytes(buf, length);
77 	oidc_debug(r, "apr_generate_random_bytes returned");
78 
79 #else
80 
81 	int fd = -1;
82 
83 	do {
84 		apr_ssize_t rc;
85 
86 		if (fd == -1) {
87 			fd = open(DEV_RANDOM, O_RDONLY);
88 			if (fd == -1)
89 				return errno;
90 		}
91 
92 		do {
93 			oidc_debug(r, "read %s for %" APR_SIZE_T_FMT " bytes", DEV_RANDOM,
94 					length);
95 			rc = read(fd, buf, length);
96 			oidc_debug(r, "read %s returned: %"APR_SIZE_T_FMT, DEV_RANDOM " bytes", rc);
97 		} while (rc == -1 && errno == EINTR);
98 
99 		if (rc < 0) {
100 			int errnum = errno;
101 			close(fd);
102 			return errnum;
103 		} else if (rc == 0) {
104 			close(fd);
105 			fd = -1; /* force open() again */
106 		} else {
107 			buf += rc;
108 			length -= rc;
109 		}
110 	} while (length > 0);
111 
112 	close(fd);
113 
114 	rv = APR_SUCCESS;
115 
116 #endif
117 
118 	return rv;
119 }
120 
121 /*
122  * generate a random string value value of a specified length
123  */
oidc_proto_generate_random_string(request_rec * r,char ** output,int len)124 static apr_byte_t oidc_proto_generate_random_string(request_rec *r,
125 		char **output, int len) {
126 	unsigned char *bytes = apr_pcalloc(r->pool, len);
127 	if (oidc_proto_generate_random_bytes(r, bytes, len) != APR_SUCCESS) {
128 		oidc_error(r, "oidc_proto_generate_random_bytes returned an error");
129 		return FALSE;
130 	}
131 	if (oidc_base64url_encode(r, output, (const char*) bytes, len, TRUE) <= 0) {
132 		oidc_error(r, "oidc_base64url_encode returned an error");
133 		return FALSE;
134 	}
135 	return TRUE;
136 }
137 
138 #define OIDC_REQUEST_OJBECT_COPY_FROM_REQUEST "copy_from_request"
139 #define OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST "copy_and_remove_from_request"
140 
141 /*
142  * indicates wether a request parameter from the authorization request needs to be
143  * copied and/or deleted to/from the protected request object based on the settings specified
144  * in the "copy_from_request"/"copy_and_remove_from_request" JSON array in the request object
145  */
oidc_proto_param_needs_action(json_t * request_object_config,const char * parameter_name,const char * action)146 static apr_byte_t oidc_proto_param_needs_action(json_t *request_object_config,
147 		const char *parameter_name, const char *action) {
148 	json_t *copy_from_request = json_object_get(request_object_config, action);
149 	size_t index = 0;
150 	while (index < json_array_size(copy_from_request)) {
151 		json_t *value = json_array_get(copy_from_request, index);
152 		if ((json_is_string(value))
153 				&& (apr_strnatcmp(json_string_value(value), parameter_name) == 0)) {
154 			return TRUE;
155 		}
156 		index++;
157 	}
158 	return FALSE;
159 }
160 
161 /* context structure for copying request parameters */
162 typedef struct oidc_proto_copy_req_ctx_t {
163 	request_rec *r;
164 	json_t *request_object_config;
165 	oidc_jwt_t *request_object;
166 	apr_table_t *params2;
167 } oidc_proto_copy_req_ctx_t;
168 
169 /*
170  * copy a parameter key/value from the authorizion request to the
171  * request object if the configuration setting says to include it
172  */
oidc_proto_copy_from_request(void * rec,const char * name,const char * value)173 static int oidc_proto_copy_from_request(void *rec, const char *name,
174 		const char *value) {
175 	oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t*) rec;
176 
177 	oidc_debug(ctx->r, "processing name: %s, value: %s", name, value);
178 
179 	if (oidc_proto_param_needs_action(ctx->request_object_config, name,
180 			OIDC_REQUEST_OJBECT_COPY_FROM_REQUEST)
181 			|| oidc_proto_param_needs_action(ctx->request_object_config, name,
182 					OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) {
183 		json_t *result = NULL;
184 		json_error_t json_error;
185 		result = json_loads(value, JSON_DECODE_ANY, &json_error);
186 		if (result == NULL)
187 			/* assume string */
188 			result = json_string(value);
189 		if (result) {
190 			json_object_set_new(ctx->request_object->payload.value.json, name,
191 					json_deep_copy(result));
192 			json_decref(result);
193 		}
194 
195 		if (oidc_proto_param_needs_action(ctx->request_object_config, name,
196 				OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) {
197 			apr_table_set(ctx->params2, name, name);
198 		}
199 
200 	}
201 
202 	return 1;
203 }
204 
205 /*
206  * delete a parameter key/value from the authorizion request if the configuration setting says to remove it
207  */
oidc_proto_delete_from_request(void * rec,const char * name,const char * value)208 static int oidc_proto_delete_from_request(void *rec, const char *name,
209 		const char *value) {
210 	oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t*) rec;
211 
212 	oidc_debug(ctx->r, "deleting from query parameters: name: %s, value: %s",
213 			name, value);
214 
215 	if (oidc_proto_param_needs_action(ctx->request_object_config, name,
216 			OIDC_REQUEST_OJBECT_COPY_AND_REMOVE_FROM_REQUEST)) {
217 		apr_table_unset(ctx->params2, name);
218 	}
219 
220 	return 1;
221 }
222 
223 /*
224  * obtain the public key for a provider to encrypt the request object with
225  */
oidc_proto_get_encryption_jwk_by_type(request_rec * r,oidc_cfg * cfg,struct oidc_provider_t * provider,int key_type,oidc_jwk_t ** jwk)226 apr_byte_t oidc_proto_get_encryption_jwk_by_type(request_rec *r, oidc_cfg *cfg,
227 		struct oidc_provider_t *provider, int key_type, oidc_jwk_t **jwk) {
228 
229 	oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
230 			provider->jwks_refresh_interval, provider->ssl_validate_server };
231 
232 	oidc_jose_error_t err;
233 	json_t *j_jwks = NULL;
234 	apr_byte_t force_refresh = TRUE;
235 	oidc_jwk_t *key = NULL;
236 	char *jwk_json = NULL;
237 
238 	/* TODO: forcefully refresh now; we may want to relax that */
239 	oidc_metadata_jwks_get(r, cfg, &jwks_uri, &j_jwks, &force_refresh);
240 
241 	if (j_jwks == NULL) {
242 		oidc_error(r, "could not retrieve JSON Web Keys");
243 		return FALSE;
244 	}
245 
246 	json_t *keys = json_object_get(j_jwks, OIDC_JWK_KEYS);
247 	if ((keys == NULL) || !(json_is_array(keys))) {
248 		oidc_error(r, "\"%s\" array element is not a JSON array",
249 				OIDC_JWK_KEYS);
250 		return FALSE;
251 	}
252 
253 	int i;
254 	/* walk the set of published keys to find the first that has a matching type */
255 	for (i = 0; i < json_array_size(keys); i++) {
256 
257 		json_t *elem = json_array_get(keys, i);
258 
259 		const char *use = json_string_value(
260 				json_object_get(elem, OIDC_JWK_USE));
261 		if ((use != NULL) && (strcmp(use, OIDC_JWK_ENC) != 0)) {
262 			oidc_debug(r, "skipping key because of non-matching \"%s\": \"%s\"",
263 					OIDC_JWK_USE, use);
264 			continue;
265 		}
266 
267 		if (oidc_jwk_parse_json(r->pool, elem, &key, &err) == FALSE) {
268 			oidc_warn(r, "oidc_jwk_parse_json failed: %s",
269 					oidc_jose_e2s(r->pool, err));
270 			continue;
271 		}
272 
273 		if (key_type == key->kty) {
274 			oidc_jwk_to_json(r->pool, key, &jwk_json, &err);
275 			oidc_debug(r, "found matching encryption key type for key: %s",
276 					jwk_json);
277 			*jwk = key;
278 			break;
279 		}
280 
281 		oidc_jwk_destroy(key);
282 	}
283 
284 	/* no need anymore for the parsed json_t contents, release the it */
285 	json_decref(j_jwks);
286 
287 	return (*jwk != NULL);
288 }
289 
oidc_key_list_first(const apr_array_header_t * key_list)290 static oidc_jwk_t* oidc_key_list_first(const apr_array_header_t *key_list) {
291 	oidc_jwk_t *rv = NULL;
292 	if ((key_list) && (key_list->nelts > 0))
293 		rv = ((oidc_jwk_t**) key_list->elts)[0];
294 	return rv;
295 }
296 
297 /*
298  * generate a request object
299  */
oidc_proto_create_request_object(request_rec * r,struct oidc_provider_t * provider,json_t * request_object_config,apr_table_t * params)300 char* oidc_proto_create_request_object(request_rec *r,
301 		struct oidc_provider_t *provider, json_t *request_object_config,
302 		apr_table_t *params) {
303 
304 	oidc_jwk_t *sjwk = NULL;
305 	int jwk_needs_destroy = 0;
306 
307 	oidc_debug(r, "enter");
308 
309 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
310 			&auth_openidc_module);
311 
312 	/* create the request object value */
313 	oidc_jwt_t *request_object = oidc_jwt_new(r->pool, TRUE, TRUE);
314 
315 	/* set basic values: iss and aud */
316 	json_object_set_new(request_object->payload.value.json, OIDC_CLAIM_ISS,
317 			json_string(provider->client_id));
318 	json_object_set_new(request_object->payload.value.json, OIDC_CLAIM_AUD,
319 			json_string(provider->issuer));
320 
321 	/* add static values to the request object as configured in the .conf file; may override iss/aud */
322 	oidc_util_json_merge(r, json_object_get(request_object_config, "static"),
323 			request_object->payload.value.json);
324 
325 	/* copy parameters from the authorization request as configured in the .conf file */
326 	apr_table_t *delete_from_query_params = apr_table_make(r->pool, 0);
327 	oidc_proto_copy_req_ctx_t data = { r, request_object_config, request_object,
328 			delete_from_query_params };
329 	apr_table_do(oidc_proto_copy_from_request, &data, params, NULL);
330 
331 	/* delete parameters from the query parameters of the authorization request as configured in the .conf file */
332 	data.params2 = params;
333 	apr_table_do(oidc_proto_delete_from_request, &data,
334 			delete_from_query_params, NULL);
335 
336 	/* debug logging */
337 	oidc_debug(r, "request object: %s",
338 			oidc_util_encode_json_object(r, request_object->payload.value.json, JSON_COMPACT));
339 
340 	char *serialized_request_object = NULL;
341 	oidc_jose_error_t err;
342 
343 	/* get the crypto settings from the configuration */
344 	json_t *crypto = json_object_get(request_object_config, "crypto");
345 	oidc_json_object_get_string(r->pool, crypto, "sign_alg",
346 			&request_object->header.alg, "none");
347 
348 	/* see if we need to sign the request object */
349 	if (strcmp(request_object->header.alg, "none") != 0) {
350 
351 		sjwk = NULL;
352 		jwk_needs_destroy = 0;
353 
354 		switch (oidc_jwt_alg2kty(request_object)) {
355 		case CJOSE_JWK_KTY_RSA:
356 			if ((provider->client_signing_keys != NULL)
357 					|| (cfg->private_keys != NULL)) {
358 				sjwk = provider->client_signing_keys ?
359 						oidc_key_list_first(provider->client_signing_keys) :
360 						oidc_key_list_first(cfg->private_keys);
361 				request_object->header.kid = apr_pstrdup(r->pool, sjwk->kid);
362 			} else {
363 				oidc_error(r,
364 						"no global or per-provider private keys have been configured to use for request object signing");
365 			}
366 			break;
367 		case CJOSE_JWK_KTY_OCT:
368 			oidc_util_create_symmetric_key(r, provider->client_secret, 0, NULL,
369 					FALSE, &sjwk);
370 			jwk_needs_destroy = 1;
371 			break;
372 		default:
373 			oidc_error(r,
374 					"unsupported signing algorithm, no key type for algorithm: %s",
375 					request_object->header.alg);
376 			break;
377 		}
378 
379 		if (sjwk == NULL) {
380 			oidc_jwt_destroy(request_object);
381 			json_decref(request_object_config);
382 			return FALSE;
383 		}
384 
385 		if (oidc_jwt_sign(r->pool, request_object, sjwk, &err) == FALSE) {
386 			oidc_error(r, "signing Request Object failed: %s",
387 					oidc_jose_e2s(r->pool, err));
388 			if (jwk_needs_destroy)
389 				oidc_jwk_destroy(sjwk);
390 			oidc_jwt_destroy(request_object);
391 			json_decref(request_object_config);
392 			return FALSE;
393 		}
394 
395 		if (jwk_needs_destroy)
396 			oidc_jwk_destroy(sjwk);
397 	}
398 
399 	oidc_jwt_t *jwe = oidc_jwt_new(r->pool, TRUE, FALSE);
400 	if (jwe == NULL) {
401 		oidc_error(r, "creating JWE failed");
402 		oidc_jwt_destroy(request_object);
403 		json_decref(request_object_config);
404 		return FALSE;
405 	}
406 
407 	oidc_json_object_get_string(r->pool, crypto, "crypt_alg", &jwe->header.alg,
408 			NULL);
409 	oidc_json_object_get_string(r->pool, crypto, "crypt_enc", &jwe->header.enc,
410 			NULL);
411 
412 	char *cser = oidc_jwt_serialize(r->pool, request_object, &err);
413 
414 	/* see if we need to encrypt the request object */
415 	if (jwe->header.alg != NULL) {
416 
417 		oidc_jwk_t *ejwk = NULL;
418 
419 		switch (oidc_jwt_alg2kty(jwe)) {
420 		case CJOSE_JWK_KTY_RSA:
421 			oidc_proto_get_encryption_jwk_by_type(r, cfg, provider,
422 					CJOSE_JWK_KTY_RSA, &ejwk);
423 			break;
424 		case CJOSE_JWK_KTY_OCT:
425 			oidc_util_create_symmetric_key(r, provider->client_secret,
426 					oidc_alg2keysize(jwe->header.alg), OIDC_JOSE_ALG_SHA256,
427 					FALSE, &ejwk);
428 			break;
429 		default:
430 			oidc_error(r,
431 					"unsupported encryption algorithm, no key type for algorithm: %s",
432 					request_object->header.alg);
433 			break;
434 		}
435 
436 		if (ejwk == NULL) {
437 			oidc_jwt_destroy(jwe);
438 			oidc_jwt_destroy(request_object);
439 			json_decref(request_object_config);
440 			return FALSE;
441 		}
442 
443 		if (jwe->header.enc == NULL)
444 			jwe->header.enc = apr_pstrdup(r->pool, CJOSE_HDR_ENC_A128CBC_HS256);
445 
446 		if (ejwk->kid != NULL)
447 			jwe->header.kid = ejwk->kid;
448 
449 		if (oidc_jwt_encrypt(r->pool, jwe, ejwk, cser,
450 				&serialized_request_object, &err) == FALSE) {
451 			oidc_error(r, "encrypting JWT failed: %s",
452 					oidc_jose_e2s(r->pool, err));
453 			oidc_jwk_destroy(ejwk);
454 			oidc_jwt_destroy(jwe);
455 			oidc_jwt_destroy(request_object);
456 			json_decref(request_object_config);
457 			return FALSE;
458 		}
459 
460 		oidc_jwk_destroy(ejwk);
461 
462 	} else {
463 
464 		/* should be sign only or "none" */
465 		serialized_request_object = cser;
466 	}
467 
468 	oidc_jwt_destroy(request_object);
469 	oidc_jwt_destroy(jwe);
470 	json_decref(request_object_config);
471 
472 	oidc_debug(r, "serialized request object JWT header = \"%s\"",
473 			oidc_proto_peek_jwt_header(r, serialized_request_object, NULL));
474 	oidc_debug(r, "serialized request object = \"%s\"",
475 			serialized_request_object);
476 
477 	return serialized_request_object;
478 }
479 
480 /*
481  * generate a request object and pass it by reference in the authorization request
482  */
oidc_proto_create_request_uri(request_rec * r,struct oidc_provider_t * provider,json_t * request_object_config,const char * redirect_uri,apr_table_t * params)483 static char* oidc_proto_create_request_uri(request_rec *r,
484 		struct oidc_provider_t *provider, json_t *request_object_config,
485 		const char *redirect_uri, apr_table_t *params) {
486 
487 	oidc_debug(r, "enter");
488 
489 	/* see if we need to override the resolver URL, mostly for test purposes */
490 	char *resolver_url = NULL;
491 	if (json_object_get(request_object_config, "url") != NULL)
492 		resolver_url = apr_pstrdup(r->pool,
493 				json_string_value(
494 						json_object_get(request_object_config, "url")));
495 	else
496 		resolver_url = apr_pstrdup(r->pool, redirect_uri);
497 
498 	char *serialized_request_object = oidc_proto_create_request_object(r,
499 			provider, request_object_config, params);
500 
501 	/* generate a temporary reference, store the request object in the cache and generate a Request URI that references it */
502 	char *request_uri = NULL;
503 	if (serialized_request_object != NULL) {
504 		char *request_ref = NULL;
505 		if (oidc_proto_generate_random_string(r, &request_ref, 16) == TRUE) {
506 			oidc_cache_set_request_uri(r, request_ref,
507 					serialized_request_object,
508 					apr_time_now() + apr_time_from_sec(OIDC_REQUEST_URI_CACHE_DURATION));
509 			request_uri = apr_psprintf(r->pool, "%s?%s=%s", resolver_url,
510 					OIDC_PROTO_REQUEST_URI, oidc_util_escape_string(r, request_ref));
511 		}
512 	}
513 
514 	return request_uri;
515 }
516 
517 /*
518  * Generic function to generate request/request_object parameter with value
519  */
oidc_proto_add_request_param(request_rec * r,struct oidc_provider_t * provider,const char * redirect_uri,apr_table_t * params)520 static void oidc_proto_add_request_param(request_rec *r,
521 		struct oidc_provider_t *provider, const char *redirect_uri,
522 		apr_table_t *params) {
523 
524 	/* parse the request object configuration from a string in to a JSON structure */
525 	json_t *request_object_config = NULL;
526 	if (oidc_util_decode_json_object(r, provider->request_object,
527 			&request_object_config) == FALSE)
528 		return;
529 
530 	/* request_uri is used as default parameter for sending Request Object */
531 	char *parameter = OIDC_PROTO_REQUEST_URI;
532 
533 	/* get request_object_type parameter from config */
534 	json_t *request_object_type = json_object_get(request_object_config,
535 			"request_object_type");
536 	if (request_object_type != NULL) {
537 		const char *request_object_type_str = json_string_value(
538 				request_object_type);
539 		if (request_object_type_str == NULL) {
540 			oidc_error(r,
541 					"Value of request_object_type in request_object config is not a string");
542 			return;
543 		}
544 
545 		/* ensure parameter variable to have a valid value */
546 		if (strcmp(request_object_type_str, OIDC_PROTO_REQUEST_OBJECT) == 0) {
547 			parameter = OIDC_PROTO_REQUEST_OBJECT;
548 		} else if (strcmp(request_object_type_str, OIDC_PROTO_REQUEST_URI)
549 				!= 0) {
550 			oidc_error(r, "Bad request_object_type in config: %s",
551 					request_object_type_str);
552 			return;
553 		}
554 	}
555 
556 	/* create request value */
557 	char *value = NULL;
558 	if (strcmp(parameter, OIDC_PROTO_REQUEST_URI) == 0) {
559 		/* parameter is "request_uri" */
560 		value = oidc_proto_create_request_uri(r, provider,
561 				request_object_config, redirect_uri, params);
562 		apr_table_set(params, OIDC_PROTO_REQUEST_URI, value);
563 	} else {
564 		/* parameter is "request" */
565 		value = oidc_proto_create_request_object(r, provider,
566 				request_object_config, params);
567 		apr_table_set(params, OIDC_PROTO_REQUEST_OBJECT, value);
568 	}
569 }
570 
571 /* context structure for encoding parameters */
572 typedef struct oidc_proto_form_post_ctx_t {
573 	request_rec *r;
574 	const char *html_body;
575 } oidc_proto_form_post_ctx_t;
576 
577 /*
578  * add a key/value pair post parameter
579  */
oidc_proto_add_form_post_param(void * rec,const char * key,const char * value)580 static int oidc_proto_add_form_post_param(void *rec, const char *key,
581 		const char *value) {
582 	oidc_proto_form_post_ctx_t *ctx = (oidc_proto_form_post_ctx_t*) rec;
583 	oidc_debug(ctx->r, "processing: %s=%s", key, value);
584 	ctx->html_body = apr_psprintf(ctx->r->pool,
585 			"%s      <input type=\"hidden\" name=\"%s\" value=\"%s\">\n",
586 			ctx->html_body, oidc_util_html_escape(ctx->r->pool, key),
587 			oidc_util_html_escape(ctx->r->pool, value));
588 	return 1;
589 }
590 
591 /*
592  * make the browser POST parameters through Javascript auto-submit
593  */
oidc_proto_html_post(request_rec * r,const char * url,apr_table_t * params)594 static int oidc_proto_html_post(request_rec *r, const char *url,
595 		apr_table_t *params) {
596 
597 	oidc_debug(r, "enter");
598 
599 	const char *html_body = apr_psprintf(r->pool,
600 			"    <p>Submitting Authentication Request...</p>\n"
601 			"    <form method=\"post\" action=\"%s\">\n"
602 			"      <p>\n", url);
603 
604 	oidc_proto_form_post_ctx_t data = { r, html_body };
605 	apr_table_do(oidc_proto_add_form_post_param, &data, params, NULL);
606 
607 	html_body = apr_psprintf(r->pool, "%s%s", data.html_body, "      </p>\n"
608 			"    </form>\n");
609 
610 	return oidc_util_html_send(r, "Submitting...", NULL,
611 			"document.forms[0].submit", html_body, OK);
612 }
613 
add_auth_request_params(request_rec * r,apr_table_t * params,const char * auth_request_params)614 void add_auth_request_params(request_rec *r, apr_table_t *params,
615 		const char *auth_request_params) {
616 	char *key = NULL;
617 	char *val = NULL;
618 
619 	if (auth_request_params == NULL)
620 		return;
621 
622 	while (*auth_request_params
623 			&& (val = ap_getword(r->pool, &auth_request_params, OIDC_CHAR_AMP))) {
624 		key = ap_getword(r->pool, (const char**) &val, OIDC_CHAR_EQUAL);
625 		ap_unescape_url(key);
626 		ap_unescape_url(val);
627 		if (apr_strnatcmp(val, OIDC_STR_HASH) != 0) {
628 			apr_table_add(params, key, val);
629 			continue;
630 		}
631 		if (oidc_util_request_has_parameter(r, key) == TRUE) {
632 			oidc_util_get_request_parameter(r, key, &val);
633 			apr_table_add(params, key, val);
634 		}
635 	}
636 }
637 
638 /*
639  * send an OpenID Connect authorization request to the specified provider
640  */
oidc_proto_authorization_request(request_rec * r,struct oidc_provider_t * provider,const char * login_hint,const char * redirect_uri,const char * state,oidc_proto_state_t * proto_state,const char * id_token_hint,const char * code_challenge,const char * auth_request_params,const char * path_scope)641 int oidc_proto_authorization_request(request_rec *r,
642 		struct oidc_provider_t *provider, const char *login_hint,
643 		const char *redirect_uri, const char *state,
644 		oidc_proto_state_t *proto_state, const char *id_token_hint,
645 		const char *code_challenge, const char *auth_request_params,
646 		const char *path_scope) {
647 
648 	/* log some stuff */
649 	oidc_debug(r,
650 			"enter, issuer=%s, redirect_uri=%s, state=%s, proto_state=%s, code_challenge=%s, auth_request_params=%s, path_scope=%s",
651 			provider->issuer, redirect_uri, state,
652 			oidc_proto_state_to_string(r, proto_state), code_challenge,
653 			auth_request_params, path_scope);
654 
655 	int rv = OK;
656 	char *authorization_request = NULL;
657 
658 	/* assemble parameters to call the token endpoint for validation */
659 	apr_table_t *params = apr_table_make(r->pool, 4);
660 
661 	/* add the response type */
662 	apr_table_setn(params, OIDC_PROTO_RESPONSE_TYPE,
663 			oidc_proto_state_get_response_type(proto_state));
664 
665 	/* concat the per-path scopes with the per-provider scopes */
666 	const char *scope = provider->scope;
667 	if (path_scope != NULL)
668 		scope = ((scope != NULL) && (apr_strnatcmp(scope, "") != 0)) ?
669 				apr_pstrcat(r->pool, scope, OIDC_STR_SPACE, path_scope,
670 						NULL) :
671 						path_scope;
672 
673 	if (scope != NULL) {
674 		if (!oidc_util_spaced_string_contains(r->pool, scope,
675 				OIDC_PROTO_SCOPE_OPENID)) {
676 			oidc_warn(r,
677 					"the configuration for the \"%s\" parameter does not include the \"%s\" scope, your provider may not return an \"id_token\": %s",
678 					OIDC_PROTO_SCOPE, OIDC_PROTO_SCOPE_OPENID, provider->scope);
679 		}
680 		apr_table_setn(params, OIDC_PROTO_SCOPE, scope);
681 	}
682 
683 	if (provider->client_id == NULL) {
684 		oidc_error(r,
685 				"no Client ID set for the provider: perhaps you are accessing an endpoint protected with \"AuthType openid-connect\" instead of \"AuthType oauth20\"?)");
686 		return HTTP_INTERNAL_SERVER_ERROR;
687 	}
688 
689 	/* add the client ID */
690 	apr_table_setn(params, OIDC_PROTO_CLIENT_ID, provider->client_id);
691 
692 	/* add the state */
693 	apr_table_setn(params, OIDC_PROTO_STATE, state);
694 
695 	/* add the redirect uri */
696 	apr_table_setn(params, OIDC_PROTO_REDIRECT_URI, redirect_uri);
697 
698 	/* add the nonce if set */
699 	const char *nonce = oidc_proto_state_get_nonce(proto_state);
700 	if (nonce != NULL)
701 		apr_table_setn(params, OIDC_PROTO_NONCE, nonce);
702 
703 	/* add PKCE code challenge if set */
704 	if (code_challenge != NULL) {
705 		apr_table_setn(params, OIDC_PROTO_CODE_CHALLENGE, code_challenge);
706 		apr_table_setn(params, OIDC_PROTO_CODE_CHALLENGE_METHOD,
707 				provider->pkce->method);
708 	}
709 
710 	/* add the response_mode if explicitly set */
711 	const char *response_mode = oidc_proto_state_get_response_mode(proto_state);
712 	if (response_mode != NULL)
713 		apr_table_setn(params, OIDC_PROTO_RESPONSE_MODE, response_mode);
714 
715 	/* add the login_hint if provided */
716 	if (login_hint != NULL)
717 		apr_table_setn(params, OIDC_PROTO_LOGIN_HINT, login_hint);
718 
719 	/* add the id_token_hint if provided */
720 	if (id_token_hint != NULL)
721 		apr_table_setn(params, OIDC_PROTO_ID_TOKEN_HINT, id_token_hint);
722 
723 	/* add the prompt setting if provided (e.g. "none" for no-GUI checks) */
724 	const char *prompt = oidc_proto_state_get_prompt(proto_state);
725 	if (prompt != NULL)
726 		apr_table_setn(params, OIDC_PROTO_PROMPT, prompt);
727 
728 	/* add any statically configured custom authorization request parameters */
729 	add_auth_request_params(r, params, provider->auth_request_params);
730 
731 	/* add any dynamically configured custom authorization request parameters */
732 	add_auth_request_params(r, params, auth_request_params);
733 
734 	/* add request parameter (request or request_uri) if set */
735 	if (provider->request_object != NULL)
736 		oidc_proto_add_request_param(r, provider, redirect_uri, params);
737 
738 	/* send the full authentication request via POST or GET */
739 	if (provider->auth_request_method == OIDC_AUTH_REQUEST_METHOD_POST) {
740 
741 		/* construct a HTML POST auto-submit page with the authorization request parameters */
742 		rv = oidc_proto_html_post(r, provider->authorization_endpoint_url,
743 				params);
744 
745 	} else if (provider->auth_request_method == OIDC_AUTH_REQUEST_METHOD_GET) {
746 
747 		/* construct the full authorization request URL */
748 		authorization_request = oidc_util_http_query_encoded_url(r,
749 				provider->authorization_endpoint_url, params);
750 
751 		// TODO: should also enable this when using the POST binding for the auth request
752 		/* see if we need to preserve POST parameters through Javascript/HTML5 storage */
753 		if (oidc_post_preserve_javascript(r, authorization_request, NULL,
754 				NULL) == FALSE) {
755 
756 			/* add the redirect location header */
757 			oidc_util_hdr_out_location_set(r, authorization_request);
758 
759 			/* and tell Apache to return an HTTP Redirect (302) message */
760 			rv = HTTP_MOVED_TEMPORARILY;
761 		}
762 	} else {
763 		oidc_error(r, "provider->auth_request_method set to wrong value: %d",
764 				provider->auth_request_method);
765 		return HTTP_INTERNAL_SERVER_ERROR;
766 	}
767 
768 	/* add a referred token binding request for the provider if enabled */
769 	if ((provider->token_binding_policy > OIDC_TOKEN_BINDING_POLICY_DISABLED)
770 			&& (oidc_util_get_provided_token_binding_id(r) != NULL))
771 		oidc_util_hdr_err_out_add(r,
772 				OIDC_HTTP_HDR_INCLUDE_REFERRED_TOKEN_BINDING_ID, "true");
773 
774 	/* cleanup */
775 	oidc_proto_state_destroy(proto_state);
776 
777 	/* no cache */
778 	oidc_util_hdr_err_out_add(r, OIDC_HTTP_HDR_CACHE_CONTROL,
779 			"no-cache, no-store, max-age=0");
780 
781 	/* log our exit code */
782 	oidc_debug(r, "return: %d", rv);
783 
784 	return rv;
785 }
786 
787 /*
788  * indicate whether the incoming HTTP POST request is an OpenID Connect Authorization Response
789  */
oidc_proto_is_post_authorization_response(request_rec * r,oidc_cfg * cfg)790 apr_byte_t oidc_proto_is_post_authorization_response(request_rec *r,
791 		oidc_cfg *cfg) {
792 
793 	/* prereq: this is a call to the configured redirect_uri; see if it is a POST */
794 	return (r->method_number == M_POST);
795 }
796 
797 /*
798  * indicate whether the incoming HTTP GET request is an OpenID Connect Authorization Response
799  */
oidc_proto_is_redirect_authorization_response(request_rec * r,oidc_cfg * cfg)800 apr_byte_t oidc_proto_is_redirect_authorization_response(request_rec *r,
801 		oidc_cfg *cfg) {
802 
803 	/* prereq: this is a call to the configured redirect_uri; see if it is a GET with state and id_token or code parameters */
804 	return ((r->method_number == M_GET)
805 			&& oidc_util_request_has_parameter(r, OIDC_PROTO_STATE)
806 			&& (oidc_util_request_has_parameter(r, OIDC_PROTO_ID_TOKEN)
807 					|| oidc_util_request_has_parameter(r, OIDC_PROTO_CODE)));
808 }
809 
810 /*
811  * generate a random value (nonce) to correlate request/response through browser state
812  */
oidc_proto_generate_nonce(request_rec * r,char ** nonce,int len)813 apr_byte_t oidc_proto_generate_nonce(request_rec *r, char **nonce, int len) {
814 	return oidc_proto_generate_random_string(r, nonce, len);
815 }
816 
817 /*
818  * PCKE "plain" proto state
819  */
oidc_proto_pkce_state_plain(request_rec * r,char ** state)820 static apr_byte_t oidc_proto_pkce_state_plain(request_rec *r, char **state) {
821 	return oidc_proto_generate_random_string(r, state,
822 			OIDC_PROTO_CODE_VERIFIER_LENGTH);
823 }
824 
825 /*
826  * PCKE "plain" code_challenge
827  */
oidc_proto_pkce_challenge_plain(request_rec * r,const char * state,char ** code_challenge)828 static apr_byte_t oidc_proto_pkce_challenge_plain(request_rec *r,
829 		const char *state, char **code_challenge) {
830 	*code_challenge = apr_pstrdup(r->pool, state);
831 	return TRUE;
832 }
833 
834 /*
835  * PCKE "plain" code_verifier
836  */
oidc_proto_pkce_verifier_plain(request_rec * r,const char * state,char ** code_verifier)837 static apr_byte_t oidc_proto_pkce_verifier_plain(request_rec *r,
838 		const char *state, char **code_verifier) {
839 	*code_verifier = apr_pstrdup(r->pool, state);
840 	return TRUE;
841 }
842 
843 /*
844  * PCKE "s256" proto state
845  */
oidc_proto_pkce_state_s256(request_rec * r,char ** state)846 static apr_byte_t oidc_proto_pkce_state_s256(request_rec *r, char **state) {
847 	return oidc_proto_generate_random_string(r, state,
848 			OIDC_PROTO_CODE_VERIFIER_LENGTH);
849 }
850 
851 /*
852  * PCKE "s256" code_challenge
853  */
oidc_proto_pkce_challenge_s256(request_rec * r,const char * state,char ** code_challenge)854 static apr_byte_t oidc_proto_pkce_challenge_s256(request_rec *r,
855 		const char *state, char **code_challenge) {
856 	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256,
857 			state, code_challenge) == FALSE) {
858 		oidc_error(r,
859 				"oidc_util_hash_string_and_base64url_encode returned an error for the code verifier");
860 		return FALSE;
861 	}
862 	return TRUE;
863 }
864 
865 /*
866  * PCKE "s256" code_verifier
867  */
oidc_proto_pkce_verifier_s256(request_rec * r,const char * state,char ** code_verifier)868 static apr_byte_t oidc_proto_pkce_verifier_s256(request_rec *r,
869 		const char *state, char **code_verifier) {
870 	*code_verifier = apr_pstrdup(r->pool, state);
871 	return TRUE;
872 }
873 
874 /*
875  * PCKE "referred_tb" proto state
876  */
oidc_proto_pkce_state_referred_tb(request_rec * r,char ** state)877 static apr_byte_t oidc_proto_pkce_state_referred_tb(request_rec *r,
878 		char **state) {
879 	*state = NULL;
880 	return TRUE;
881 }
882 
883 /*
884  * PCKE "referred_tb" code_challenge
885  */
oidc_proto_pkce_challenge_referred_tb(request_rec * r,const char * state,char ** code_challenge)886 static apr_byte_t oidc_proto_pkce_challenge_referred_tb(request_rec *r,
887 		const char *state, char **code_challenge) {
888 	// state should be NULL
889 	*code_challenge = OIDC_PKCE_METHOD_REFERRED_TB;
890 	return TRUE;
891 }
892 
893 /*
894  * PCKE "referred_tb" code_verifier
895  */
oidc_proto_pkce_verifier_referred_tb(request_rec * r,const char * state,char ** code_verifier)896 static apr_byte_t oidc_proto_pkce_verifier_referred_tb(request_rec *r,
897 		const char *state, char **code_verifier) {
898 	const char *tb_id = oidc_util_get_provided_token_binding_id(r);
899 	*code_verifier = tb_id ? apr_pstrdup(r->pool, tb_id) : NULL;
900 	return TRUE;
901 }
902 
903 /*
904  * PKCE plain
905  */
906 oidc_proto_pkce_t oidc_pkce_plain = {
907 		OIDC_PKCE_METHOD_PLAIN,
908 		oidc_proto_pkce_state_plain,
909 		oidc_proto_pkce_verifier_plain,
910 		oidc_proto_pkce_challenge_plain
911 };
912 
913 /*
914  * PKCE s256
915  */
916 oidc_proto_pkce_t oidc_pkce_s256 = {
917 		OIDC_PKCE_METHOD_S256,
918 		oidc_proto_pkce_state_s256,
919 		oidc_proto_pkce_verifier_s256,
920 		oidc_proto_pkce_challenge_s256
921 };
922 
923 /*
924  * PKCE referred_tb
925  */
926 oidc_proto_pkce_t oidc_pkce_referred_tb = {
927 		OIDC_PKCE_METHOD_REFERRED_TB,
928 		oidc_proto_pkce_state_referred_tb,
929 		oidc_proto_pkce_verifier_referred_tb,
930 		oidc_proto_pkce_challenge_referred_tb };
931 
932 #define OIDC_PROTO_STATE_ISSUER          "i"
933 #define OIDC_PROTO_STATE_ORIGINAL_URL    "ou"
934 #define OIDC_PROTO_STATE_ORIGINAL_METHOD "om"
935 #define OIDC_PROTO_STATE_RESPONSE_MODE   "rm"
936 #define OIDC_PROTO_STATE_RESPONSE_TYPE   "rt"
937 #define OIDC_PROTO_STATE_NONCE           "n"
938 #define OIDC_PROTO_STATE_TIMESTAMP       "t"
939 #define OIDC_PROTO_STATE_PROMPT          "pr"
940 #define OIDC_PROTO_STATE_PKCE_STATE      "ps"
941 #define OIDC_PROTO_STATE_STATE           "s"
942 
oidc_proto_state_get_string_value(oidc_proto_state_t * proto_state,const char * name)943 static const char* oidc_proto_state_get_string_value(
944 		oidc_proto_state_t *proto_state, const char *name) {
945 	json_t *v = json_object_get(proto_state, name);
946 	return v ? json_string_value(v) : NULL;
947 }
948 
oidc_proto_state_set_string_value(oidc_proto_state_t * proto_state,const char * name,const char * value)949 static void oidc_proto_state_set_string_value(oidc_proto_state_t *proto_state,
950 		const char *name, const char *value) {
951 	json_object_set_new(proto_state, name, json_string(value));
952 }
953 
oidc_proto_state_new()954 oidc_proto_state_t* oidc_proto_state_new() {
955 	return json_object();
956 }
957 
oidc_proto_state_destroy(oidc_proto_state_t * proto_state)958 void oidc_proto_state_destroy(oidc_proto_state_t *proto_state) {
959 	json_decref(proto_state);
960 }
961 
oidc_proto_state_from_cookie(request_rec * r,oidc_cfg * c,const char * cookieValue)962 oidc_proto_state_t* oidc_proto_state_from_cookie(request_rec *r, oidc_cfg *c,
963 		const char *cookieValue) {
964 	json_t *result = NULL;
965 	oidc_util_jwt_verify(r, c->crypto_passphrase, cookieValue, &result);
966 	return result;
967 }
968 
oidc_proto_state_to_cookie(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state)969 char* oidc_proto_state_to_cookie(request_rec *r, oidc_cfg *c,
970 		oidc_proto_state_t *proto_state) {
971 	char *cookieValue = NULL;
972 	oidc_util_jwt_create(r, c->crypto_passphrase, proto_state, &cookieValue);
973 	return cookieValue;
974 }
oidc_proto_state_to_string(request_rec * r,oidc_proto_state_t * proto_state)975 char* oidc_proto_state_to_string(request_rec *r,
976 		oidc_proto_state_t *proto_state) {
977 	return oidc_util_encode_json_object(r, proto_state, JSON_COMPACT);
978 }
979 
oidc_proto_state_get_issuer(oidc_proto_state_t * proto_state)980 const char* oidc_proto_state_get_issuer(oidc_proto_state_t *proto_state) {
981 	return oidc_proto_state_get_string_value(proto_state,
982 			OIDC_PROTO_STATE_ISSUER);
983 }
984 
oidc_proto_state_get_nonce(oidc_proto_state_t * proto_state)985 const char* oidc_proto_state_get_nonce(oidc_proto_state_t *proto_state) {
986 	return oidc_proto_state_get_string_value(proto_state,
987 			OIDC_PROTO_STATE_NONCE);
988 }
989 
oidc_proto_state_get_timestamp(oidc_proto_state_t * proto_state)990 apr_time_t oidc_proto_state_get_timestamp(oidc_proto_state_t *proto_state) {
991 	json_t *v = json_object_get(proto_state, OIDC_PROTO_STATE_TIMESTAMP);
992 	return v ? apr_time_from_sec(json_integer_value(v)) : -1;
993 }
994 
oidc_proto_state_get_prompt(oidc_proto_state_t * proto_state)995 const char* oidc_proto_state_get_prompt(oidc_proto_state_t *proto_state) {
996 	return oidc_proto_state_get_string_value(proto_state,
997 			OIDC_PROTO_STATE_PROMPT);
998 }
999 
oidc_proto_state_get_response_type(oidc_proto_state_t * proto_state)1000 const char* oidc_proto_state_get_response_type(oidc_proto_state_t *proto_state) {
1001 	return oidc_proto_state_get_string_value(proto_state,
1002 			OIDC_PROTO_STATE_RESPONSE_TYPE);
1003 }
1004 
oidc_proto_state_get_response_mode(oidc_proto_state_t * proto_state)1005 const char* oidc_proto_state_get_response_mode(oidc_proto_state_t *proto_state) {
1006 	return oidc_proto_state_get_string_value(proto_state,
1007 			OIDC_PROTO_STATE_RESPONSE_MODE);
1008 }
1009 
oidc_proto_state_get_original_url(oidc_proto_state_t * proto_state)1010 const char* oidc_proto_state_get_original_url(oidc_proto_state_t *proto_state) {
1011 	return oidc_proto_state_get_string_value(proto_state,
1012 			OIDC_PROTO_STATE_ORIGINAL_URL);
1013 }
1014 
oidc_proto_state_get_original_method(oidc_proto_state_t * proto_state)1015 const char* oidc_proto_state_get_original_method(
1016 		oidc_proto_state_t *proto_state) {
1017 	return oidc_proto_state_get_string_value(proto_state,
1018 			OIDC_PROTO_STATE_ORIGINAL_METHOD);
1019 }
1020 
oidc_proto_state_get_state(oidc_proto_state_t * proto_state)1021 const char* oidc_proto_state_get_state(oidc_proto_state_t *proto_state) {
1022 	return oidc_proto_state_get_string_value(proto_state,
1023 			OIDC_PROTO_STATE_STATE);
1024 }
1025 
oidc_proto_state_get_pkce_state(oidc_proto_state_t * proto_state)1026 const char* oidc_proto_state_get_pkce_state(oidc_proto_state_t *proto_state) {
1027 	return oidc_proto_state_get_string_value(proto_state,
1028 			OIDC_PROTO_STATE_PKCE_STATE);
1029 }
1030 
oidc_proto_state_set_state(oidc_proto_state_t * proto_state,const char * state)1031 void oidc_proto_state_set_state(oidc_proto_state_t *proto_state,
1032 		const char *state) {
1033 	oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_STATE,
1034 			state);
1035 }
1036 
oidc_proto_state_set_issuer(oidc_proto_state_t * proto_state,const char * issuer)1037 void oidc_proto_state_set_issuer(oidc_proto_state_t *proto_state,
1038 		const char *issuer) {
1039 	oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_ISSUER,
1040 			issuer);
1041 }
1042 
oidc_proto_state_set_original_url(oidc_proto_state_t * proto_state,const char * original_url)1043 void oidc_proto_state_set_original_url(oidc_proto_state_t *proto_state,
1044 		const char *original_url) {
1045 	oidc_proto_state_set_string_value(proto_state,
1046 			OIDC_PROTO_STATE_ORIGINAL_URL, original_url);
1047 }
1048 
oidc_proto_state_set_original_method(oidc_proto_state_t * proto_state,const char * original_method)1049 void oidc_proto_state_set_original_method(oidc_proto_state_t *proto_state,
1050 		const char *original_method) {
1051 	oidc_proto_state_set_string_value(proto_state,
1052 			OIDC_PROTO_STATE_ORIGINAL_METHOD, original_method);
1053 }
1054 
oidc_proto_state_set_response_mode(oidc_proto_state_t * proto_state,const char * response_mode)1055 void oidc_proto_state_set_response_mode(oidc_proto_state_t *proto_state,
1056 		const char *response_mode) {
1057 	oidc_proto_state_set_string_value(proto_state,
1058 			OIDC_PROTO_STATE_RESPONSE_MODE, response_mode);
1059 }
1060 
oidc_proto_state_set_response_type(oidc_proto_state_t * proto_state,const char * response_type)1061 void oidc_proto_state_set_response_type(oidc_proto_state_t *proto_state,
1062 		const char *response_type) {
1063 	oidc_proto_state_set_string_value(proto_state,
1064 			OIDC_PROTO_STATE_RESPONSE_TYPE, response_type);
1065 }
1066 
oidc_proto_state_set_nonce(oidc_proto_state_t * proto_state,const char * nonce)1067 void oidc_proto_state_set_nonce(oidc_proto_state_t *proto_state,
1068 		const char *nonce) {
1069 	oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_NONCE,
1070 			nonce);
1071 }
1072 
oidc_proto_state_set_prompt(oidc_proto_state_t * proto_state,const char * prompt)1073 void oidc_proto_state_set_prompt(oidc_proto_state_t *proto_state,
1074 		const char *prompt) {
1075 	oidc_proto_state_set_string_value(proto_state, OIDC_PROTO_STATE_PROMPT,
1076 			prompt);
1077 }
1078 
oidc_proto_state_set_pkce_state(oidc_proto_state_t * proto_state,const char * pkce_state)1079 void oidc_proto_state_set_pkce_state(oidc_proto_state_t *proto_state,
1080 		const char *pkce_state) {
1081 	oidc_proto_state_set_string_value(proto_state,
1082 			OIDC_PROTO_STATE_PKCE_STATE, pkce_state);
1083 }
1084 
oidc_proto_state_set_timestamp_now(oidc_proto_state_t * proto_state)1085 void oidc_proto_state_set_timestamp_now(oidc_proto_state_t *proto_state) {
1086 	json_object_set_new(proto_state, OIDC_PROTO_STATE_TIMESTAMP,
1087 			json_integer(apr_time_sec(apr_time_now())));
1088 }
1089 
1090 /*
1091  * if a nonce was passed in the authorization request (and stored in the browser state),
1092  * check that it matches the nonce value in the id_token payload
1093  */
1094 // non-static for test.c
oidc_proto_validate_nonce(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * nonce,oidc_jwt_t * jwt)1095 apr_byte_t oidc_proto_validate_nonce(request_rec *r, oidc_cfg *cfg,
1096 		oidc_provider_t *provider, const char *nonce, oidc_jwt_t *jwt) {
1097 
1098 	oidc_jose_error_t err;
1099 
1100 	/* see if we have this nonce cached already */
1101 	char *replay = NULL;
1102 	oidc_cache_get_nonce(r, nonce, &replay);
1103 	if (replay != NULL) {
1104 		oidc_error(r,
1105 				"the nonce value (%s) passed in the browser state was found in the cache already; possible replay attack!?",
1106 				nonce);
1107 		return FALSE;
1108 	}
1109 
1110 	/* get the "nonce" value in the id_token payload */
1111 	char *j_nonce = NULL;
1112 	if (oidc_jose_get_string(r->pool, jwt->payload.value.json,
1113 			OIDC_CLAIM_NONCE,
1114 			TRUE, &j_nonce, &err) == FALSE) {
1115 		oidc_error(r,
1116 				"id_token JSON payload did not contain a \"%s\" string: %s",
1117 				OIDC_CLAIM_NONCE, oidc_jose_e2s(r->pool, err));
1118 		return FALSE;
1119 	}
1120 
1121 	/* see if the nonce in the id_token matches the one that we sent in the authorization request */
1122 	if (apr_strnatcmp(nonce, j_nonce) != 0) {
1123 		oidc_error(r,
1124 				"the nonce value (%s) in the id_token did not match the one stored in the browser session (%s)",
1125 				j_nonce, nonce);
1126 		return FALSE;
1127 	}
1128 
1129 	/*
1130 	 * nonce cache duration (replay prevention window) is the 2x the configured
1131 	 * slack on the timestamp (+-) for token issuance plus 10 seconds for safety
1132 	 */
1133 	apr_time_t nonce_cache_duration = apr_time_from_sec(
1134 			provider->idtoken_iat_slack * 2 + 10);
1135 
1136 	/* store it in the cache for the calculated duration */
1137 	oidc_cache_set_nonce(r, nonce, nonce,
1138 			apr_time_now() + nonce_cache_duration);
1139 
1140 	oidc_debug(r,
1141 			"nonce \"%s\" validated successfully and is now cached for %" APR_TIME_T_FMT " seconds",
1142 			nonce, apr_time_sec(nonce_cache_duration));
1143 
1144 	return TRUE;
1145 }
1146 
1147 /*
1148  * validate the "aud" and "azp" claims in the id_token payload
1149  */
oidc_proto_validate_aud_and_azp(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,oidc_jwt_payload_t * id_token_payload)1150 apr_byte_t oidc_proto_validate_aud_and_azp(request_rec *r, oidc_cfg *cfg,
1151 		oidc_provider_t *provider, oidc_jwt_payload_t *id_token_payload) {
1152 
1153 	char *azp = NULL;
1154 	oidc_jose_get_string(r->pool, id_token_payload->value.json,
1155 			OIDC_CLAIM_AZP,
1156 			FALSE, &azp,
1157 			NULL);
1158 
1159 	/*
1160 	 * the "azp" claim is only needed when the id_token has a single audience value and that audience
1161 	 * is different than the authorized party; it MAY be included even when the authorized party is
1162 	 * the same as the sole audience.
1163 	 */
1164 	if ((azp != NULL) && (apr_strnatcmp(azp, provider->client_id) != 0)) {
1165 		oidc_error(r,
1166 				"the \"%s\" claim (%s) is present in the id_token, but is not equal to the configured client_id (%s)",
1167 				OIDC_CLAIM_AZP, azp, provider->client_id);
1168 		return FALSE;
1169 	}
1170 
1171 	/* get the "aud" value from the JSON payload */
1172 	json_t *aud = json_object_get(id_token_payload->value.json,
1173 			OIDC_CLAIM_AUD);
1174 	if (aud != NULL) {
1175 
1176 		/* check if it is a single-value */
1177 		if (json_is_string(aud)) {
1178 
1179 			/* a single-valued audience must be equal to our client_id */
1180 			if (apr_strnatcmp(json_string_value(aud), provider->client_id)
1181 					!= 0) {
1182 				oidc_error(r,
1183 						"the configured client_id (%s) did not match the \"%s\" claim value (%s) in the id_token",
1184 						provider->client_id, OIDC_CLAIM_AUD,
1185 						json_string_value(aud));
1186 				return FALSE;
1187 			}
1188 
1189 			/* check if this is a multi-valued audience */
1190 		} else if (json_is_array(aud)) {
1191 
1192 			if ((json_array_size(aud) > 1) && (azp == NULL)) {
1193 				oidc_debug(r,
1194 						"the \"%s\" claim value in the id_token is an array with more than 1 element, but \"%s\" claim is not present (a SHOULD in the spec...)",
1195 						OIDC_CLAIM_AUD, OIDC_CLAIM_AZP);
1196 			}
1197 
1198 			if (oidc_util_json_array_has_value(r, aud,
1199 					provider->client_id) == FALSE) {
1200 				oidc_error(r,
1201 						"our configured client_id (%s) could not be found in the array of values for \"%s\" claim",
1202 						provider->client_id, OIDC_CLAIM_AUD);
1203 				return FALSE;
1204 			}
1205 		} else {
1206 			oidc_error(r,
1207 					"id_token JSON payload \"%s\" claim is not a string nor an array",
1208 					OIDC_CLAIM_AUD);
1209 			return FALSE;
1210 		}
1211 
1212 	} else {
1213 		oidc_error(r, "id_token JSON payload did not contain an \"%s\" claim",
1214 				OIDC_CLAIM_AUD);
1215 		return FALSE;
1216 	}
1217 
1218 	return TRUE;
1219 }
1220 
1221 /*
1222  * validate "iat" claim in JWT
1223  */
oidc_proto_validate_iat(request_rec * r,oidc_jwt_t * jwt,apr_byte_t is_mandatory,int slack)1224 static apr_byte_t oidc_proto_validate_iat(request_rec *r, oidc_jwt_t *jwt,
1225 		apr_byte_t is_mandatory, int slack) {
1226 
1227 	/* get the current time */
1228 	apr_time_t now = apr_time_sec(apr_time_now());
1229 
1230 	/* sanity check for iat being set */
1231 	if (jwt->payload.iat == OIDC_JWT_CLAIM_TIME_EMPTY) {
1232 		if (is_mandatory) {
1233 			oidc_error(r, "JWT did not contain an \"%s\" number value",
1234 					OIDC_CLAIM_IAT);
1235 			return FALSE;
1236 		}
1237 		return TRUE;
1238 	}
1239 
1240 	/* see if we are asked to enforce a time window at all */
1241 	if (slack < 0) {
1242 		oidc_debug(r, "slack for JWT set < 0, do not enforce boundary check");
1243 		return TRUE;
1244 	}
1245 
1246 	/* check if this id_token has been issued just now +- slack (default 10 minutes) */
1247 	if ((now - slack) > jwt->payload.iat) {
1248 		oidc_error(r,
1249 				"\"iat\" validation failure (%ld): JWT was issued more than %d seconds ago",
1250 				(long )jwt->payload.iat, slack);
1251 		return FALSE;
1252 	}
1253 	if ((now + slack) < jwt->payload.iat) {
1254 		oidc_error(r,
1255 				"\"iat\" validation failure (%ld): JWT was issued more than %d seconds in the future",
1256 				(long )jwt->payload.iat, slack);
1257 		return FALSE;
1258 	}
1259 
1260 	return TRUE;
1261 }
1262 
1263 /*
1264  * validate "exp" claim in JWT
1265  */
oidc_proto_validate_exp(request_rec * r,oidc_jwt_t * jwt,apr_byte_t is_mandatory)1266 static apr_byte_t oidc_proto_validate_exp(request_rec *r, oidc_jwt_t *jwt,
1267 		apr_byte_t is_mandatory) {
1268 
1269 	/* get the current time */
1270 	apr_time_t now = apr_time_sec(apr_time_now());
1271 
1272 	/* sanity check for exp being set */
1273 	if (jwt->payload.exp == OIDC_JWT_CLAIM_TIME_EMPTY) {
1274 		if (is_mandatory) {
1275 			oidc_error(r, "JWT did not contain an \"%s\" number value",
1276 					OIDC_CLAIM_EXP);
1277 			return FALSE;
1278 		}
1279 		return TRUE;
1280 	}
1281 
1282 	/* see if now is beyond the JWT expiry timestamp */
1283 	apr_time_t expires = jwt->payload.exp;
1284 	if (now > expires) {
1285 		oidc_error(r,
1286 				"\"exp\" validation failure (%ld): JWT expired %ld seconds ago",
1287 				(long )expires, (long )(now - expires));
1288 		return FALSE;
1289 	}
1290 
1291 	return TRUE;
1292 }
1293 
1294 /*
1295  * validate a JSON Web token
1296  */
oidc_proto_validate_jwt(request_rec * r,oidc_jwt_t * jwt,const char * iss,apr_byte_t exp_is_mandatory,apr_byte_t iat_is_mandatory,int iat_slack,int token_binding_policy)1297 apr_byte_t oidc_proto_validate_jwt(request_rec *r, oidc_jwt_t *jwt,
1298 		const char *iss, apr_byte_t exp_is_mandatory,
1299 		apr_byte_t iat_is_mandatory, int iat_slack, int token_binding_policy) {
1300 
1301 	if (iss != NULL) {
1302 
1303 		/* issuer is set and must match */
1304 		if (jwt->payload.iss == NULL) {
1305 			oidc_error(r,
1306 					"JWT did not contain an \"%s\" string (requested value: %s)",
1307 					OIDC_CLAIM_ISS, iss);
1308 			return FALSE;
1309 		}
1310 
1311 		/* check if the issuer matches the requested value */
1312 		if (oidc_util_issuer_match(iss, jwt->payload.iss) == FALSE) {
1313 			oidc_error(r,
1314 					"requested issuer (%s) does not match received \"%s\" value in id_token (%s)",
1315 					iss, OIDC_CLAIM_ISS, jwt->payload.iss);
1316 			return FALSE;
1317 		}
1318 	}
1319 
1320 	/* check exp */
1321 	if (oidc_proto_validate_exp(r, jwt, exp_is_mandatory) == FALSE)
1322 		return FALSE;
1323 
1324 	/* check iat */
1325 	if (oidc_proto_validate_iat(r, jwt, iat_is_mandatory, iat_slack) == FALSE)
1326 		return FALSE;
1327 
1328 	/* check the token binding ID in the JWT */
1329 	if (oidc_util_json_validate_cnf(r, jwt->payload.value.json,
1330 			token_binding_policy) == FALSE)
1331 		return FALSE;
1332 
1333 	return TRUE;
1334 }
1335 
1336 /*
1337  * check whether the provided JWT is a valid id_token for the specified "provider"
1338  */
oidc_proto_validate_idtoken(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * nonce)1339 static apr_byte_t oidc_proto_validate_idtoken(request_rec *r,
1340 		oidc_provider_t *provider, oidc_jwt_t *jwt, const char *nonce) {
1341 
1342 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
1343 			&auth_openidc_module);
1344 
1345 	oidc_debug(r, "enter, jwt.header=\"%s\", jwt.payload=\"%s\", nonce=\"%s\"",
1346 			jwt->header.value.str, jwt->payload.value.str, nonce);
1347 
1348 	/* if a nonce is not passed, we're doing a ("code") flow where the nonce is optional */
1349 	if (nonce != NULL) {
1350 		/* if present, verify the nonce */
1351 		if (oidc_proto_validate_nonce(r, cfg, provider, nonce, jwt) == FALSE)
1352 			return FALSE;
1353 	}
1354 
1355 	/* validate the ID Token JWT, requiring iss match, and valid exp + iat */
1356 	if (oidc_proto_validate_jwt(r, jwt,
1357 			provider->validate_issuer ? provider->issuer : NULL, TRUE, TRUE,
1358 					provider->idtoken_iat_slack,
1359 					provider->token_binding_policy) == FALSE)
1360 		return FALSE;
1361 
1362 	/* check if the required-by-spec "sub" claim is present */
1363 	if (jwt->payload.sub == NULL) {
1364 		oidc_error(r,
1365 				"id_token JSON payload did not contain the required-by-spec \"%s\" string value",
1366 				OIDC_CLAIM_SUB);
1367 		return FALSE;
1368 	}
1369 
1370 	/* verify the "aud" and "azp" values */
1371 	if (oidc_proto_validate_aud_and_azp(r, cfg, provider,
1372 			&jwt->payload) == FALSE)
1373 		return FALSE;
1374 
1375 	return TRUE;
1376 }
1377 
1378 /*
1379  * get the key from the JWKs that corresponds with the key specified in the header
1380  */
oidc_proto_get_key_from_jwks(request_rec * r,oidc_jwt_t * jwt,json_t * j_jwks,apr_hash_t * result)1381 static apr_byte_t oidc_proto_get_key_from_jwks(request_rec *r, oidc_jwt_t *jwt,
1382 		json_t *j_jwks, apr_hash_t *result) {
1383 
1384 	apr_byte_t rc = TRUE;
1385 	oidc_jwk_t *jwk = NULL;
1386 	oidc_jose_error_t err;
1387 	char *jwk_json = NULL;
1388 
1389 	/* get the (optional) thumbprint for comparison */
1390 	const char *x5t = oidc_jwt_hdr_get(jwt, OIDC_JWK_X5T);
1391 	oidc_debug(r, "search for kid \"%s\" or thumbprint x5t \"%s\"",
1392 			jwt->header.kid, x5t);
1393 
1394 	/* get the "keys" JSON array from the JWKs object */
1395 	json_t *keys = json_object_get(j_jwks, OIDC_JWK_KEYS);
1396 	if ((keys == NULL) || !(json_is_array(keys))) {
1397 		oidc_error(r, "\"%s\" array element is not a JSON array",
1398 				OIDC_JWK_KEYS);
1399 		return FALSE;
1400 	}
1401 
1402 	int i;
1403 	for (i = 0; i < json_array_size(keys); i++) {
1404 
1405 		/* get the next element in the array */
1406 		json_t *elem = json_array_get(keys, i);
1407 
1408 		if (oidc_jwk_parse_json(r->pool, elem, &jwk, &err) == FALSE) {
1409 			oidc_warn(r, "oidc_jwk_parse_json failed: %s",
1410 					oidc_jose_e2s(r->pool, err));
1411 			continue;
1412 		}
1413 
1414 		/* get the key type and see if it is the type that we are looking for */
1415 		if (oidc_jwt_alg2kty(jwt) != jwk->kty) {
1416 			oidc_debug(r,
1417 					"skipping non matching kty=%d for kid=%s because it doesn't match requested kty=%d, kid=%s",
1418 					jwk->kty, jwk->kid, oidc_jwt_alg2kty(jwt), jwt->header.kid);
1419 			oidc_jwk_destroy(jwk);
1420 			continue;
1421 		}
1422 
1423 		/* see if we were looking for a specific kid, if not we'll include any key that matches the type */
1424 		if ((jwt->header.kid == NULL) && (x5t == NULL)) {
1425 			const char *use = json_string_value(
1426 					json_object_get(elem, OIDC_JWK_USE));
1427 			if ((use != NULL) && (strcmp(use, OIDC_JWK_SIG) != 0)) {
1428 				oidc_debug(r,
1429 						"skipping key because of non-matching \"%s\": \"%s\"",
1430 						OIDC_JWK_USE, use);
1431 				oidc_jwk_destroy(jwk);
1432 			} else {
1433 				oidc_jwk_to_json(r->pool, jwk, &jwk_json, &err);
1434 				oidc_debug(r,
1435 						"no kid/x5t to match, include matching key type: %s",
1436 						jwk_json);
1437 				if (jwk->kid != NULL)
1438 					apr_hash_set(result, jwk->kid, APR_HASH_KEY_STRING, jwk);
1439 				else
1440 					// can do this because we never remove anything from the list
1441 					apr_hash_set(result,
1442 							apr_psprintf(r->pool, "%d", apr_hash_count(result)),
1443 							APR_HASH_KEY_STRING, jwk);
1444 			}
1445 			continue;
1446 		}
1447 
1448 		/* we are looking for a specific kid, get the kid from the current element */
1449 		/* compare the requested kid against the current element */
1450 		if ((jwt->header.kid != NULL) && (jwk->kid != NULL)
1451 				&& (apr_strnatcmp(jwt->header.kid, jwk->kid) == 0)) {
1452 			oidc_jwk_to_json(r->pool, jwk, &jwk_json, &err);
1453 			oidc_debug(r, "found matching kid: \"%s\" for jwk: %s",
1454 					jwt->header.kid, jwk_json);
1455 			apr_hash_set(result, jwt->header.kid, APR_HASH_KEY_STRING, jwk);
1456 			break;
1457 		}
1458 
1459 		/* we are looking for a specific x5t, get the x5t from the current element */
1460 		char *s_x5t = NULL;
1461 		oidc_json_object_get_string(r->pool, elem, OIDC_JWK_X5T, &s_x5t,
1462 				NULL);
1463 		/* compare the requested thumbprint against the current element */
1464 		if ((s_x5t != NULL) && (x5t != NULL)
1465 				&& (apr_strnatcmp(x5t, s_x5t) == 0)) {
1466 			oidc_jwk_to_json(r->pool, jwk, &jwk_json, &err);
1467 			oidc_debug(r, "found matching %s: \"%s\" for jwk: %s", OIDC_JWK_X5T,
1468 					x5t, jwk_json);
1469 			apr_hash_set(result, x5t, APR_HASH_KEY_STRING, jwk);
1470 			break;
1471 		}
1472 
1473 		/* the right key type but no matching kid/x5t */
1474 		oidc_jwk_destroy(jwk);
1475 	}
1476 
1477 	return rc;
1478 }
1479 
1480 /*
1481  * get the keys from the (possibly cached) set of JWKs on the jwk_uri that corresponds with the key specified in the header
1482  */
oidc_proto_get_keys_from_jwks_uri(request_rec * r,oidc_cfg * cfg,oidc_jwt_t * jwt,const oidc_jwks_uri_t * jwks_uri,apr_hash_t * keys,apr_byte_t * force_refresh)1483 apr_byte_t oidc_proto_get_keys_from_jwks_uri(request_rec *r, oidc_cfg *cfg,
1484 		oidc_jwt_t *jwt, const oidc_jwks_uri_t *jwks_uri, apr_hash_t *keys,
1485 		apr_byte_t *force_refresh) {
1486 
1487 	json_t *j_jwks = NULL;
1488 
1489 	/* get the set of JSON Web Keys for this provider (possibly by downloading them from the specified provider->jwk_uri) */
1490 	oidc_metadata_jwks_get(r, cfg, jwks_uri, &j_jwks, force_refresh);
1491 	if (j_jwks == NULL) {
1492 		oidc_error(r, "could not %s JSON Web Keys",
1493 				*force_refresh ? "refresh" : "get");
1494 		return FALSE;
1495 	}
1496 
1497 	/*
1498 	 * get the key corresponding to the kid from the header, referencing the key that
1499 	 * was used to sign this message (or get all keys in case no kid was set)
1500 	 *
1501 	 * we don't check the error return value because we'll treat "error" in the same
1502 	 * way as "key not found" i.e. by refreshing the keys from the JWKs URI if not
1503 	 * already done
1504 	 */
1505 	oidc_proto_get_key_from_jwks(r, jwt, j_jwks, keys);
1506 
1507 	/* no need anymore for the parsed json_t contents, release the it */
1508 	json_decref(j_jwks);
1509 
1510 	/* if we've got no keys and we did not do a fresh download, then the cache may be stale */
1511 	if ((apr_hash_count(keys) < 1) && (*force_refresh == FALSE)) {
1512 
1513 		/* we did not get a key, but we have not refreshed the JWKs from the jwks_uri yet */
1514 		oidc_warn(r,
1515 				"could not find a key in the cached JSON Web Keys, doing a forced refresh in case keys were rolled over");
1516 		/* get the set of JSON Web Keys forcing a fresh download from the specified JWKs URI */
1517 		*force_refresh = TRUE;
1518 		return oidc_proto_get_keys_from_jwks_uri(r, cfg, jwt, jwks_uri, keys,
1519 				force_refresh);
1520 	}
1521 
1522 	oidc_debug(r,
1523 			"returning %d key(s) obtained from the (possibly cached) JWKs URI",
1524 			apr_hash_count(keys));
1525 
1526 	return TRUE;
1527 }
1528 
1529 /*
1530  * verify the signature on a JWT using the dynamically obtained and statically configured keys
1531  */
oidc_proto_jwt_verify(request_rec * r,oidc_cfg * cfg,oidc_jwt_t * jwt,const oidc_jwks_uri_t * jwks_uri,apr_hash_t * static_keys,const char * alg)1532 apr_byte_t oidc_proto_jwt_verify(request_rec *r, oidc_cfg *cfg, oidc_jwt_t *jwt,
1533 		const oidc_jwks_uri_t *jwks_uri, apr_hash_t *static_keys,
1534 		const char *alg) {
1535 
1536 	oidc_jose_error_t err;
1537 	apr_hash_t *dynamic_keys = NULL;
1538 
1539 	if (alg != NULL) {
1540 		if (apr_strnatcmp(jwt->header.alg, alg) != 0) {
1541 			oidc_error(r,
1542 					"JWT was not signed with the expected configured algorithm: %s != %s",
1543 					jwt->header.alg, alg);
1544 			return FALSE;
1545 		}
1546 	}
1547 
1548 	dynamic_keys = apr_hash_make(r->pool);
1549 
1550 	/* see if we've got a JWKs URI set for signature validation with dynamically obtained asymmetric keys */
1551 	if (jwks_uri->url == NULL) {
1552 		oidc_debug(r,
1553 				"\"jwks_uri\" is not set, signature validation will only be performed against statically configured keys");
1554 		/* the JWKs URI was provided, but let's see if it makes sense to pull down keys, i.e. if it is an asymmetric signature */
1555 	} /*else if (oidc_jose_signature_is_hmac(r->pool, jwt)) {
1556 	 oidc_debug(r,
1557 	 "\"jwks_uri\" is set, but the JWT has a symmetric signature so we won't pull/use keys from there");
1558 	 } */else {
1559 		 apr_byte_t force_refresh = jwt->header.kid == NULL ? TRUE : FALSE;
1560 		 /* get the key from the JWKs that corresponds with the key specified in the header */
1561 		 if (oidc_proto_get_keys_from_jwks_uri(r, cfg, jwt, jwks_uri,
1562 				 dynamic_keys, &force_refresh) == FALSE) {
1563 			 oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
1564 			 return FALSE;
1565 		 }
1566 	 }
1567 
1568 	/* do the actual JWS verification with the locally and remotely provided key material */
1569 	// TODO: now static keys "win" if the same `kid` was used in both local and remote key sets
1570 	if (oidc_jwt_verify(r->pool, jwt,
1571 			oidc_util_merge_key_sets_hash(r->pool, static_keys, dynamic_keys),
1572 			&err) == FALSE) {
1573 		oidc_error(r, "JWT signature verification failed: %s",
1574 				oidc_jose_e2s(r->pool, err));
1575 		oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
1576 		return FALSE;
1577 	}
1578 
1579 	oidc_debug(r,
1580 			"JWT signature verification with algorithm \"%s\" was successful",
1581 			jwt->header.alg);
1582 
1583 	oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
1584 	return TRUE;
1585 }
1586 
1587 /*
1588  * return the compact-encoded JWT header contents
1589  */
oidc_proto_peek_jwt_header(request_rec * r,const char * compact_encoded_jwt,char ** alg)1590 char* oidc_proto_peek_jwt_header(request_rec *r,
1591 		const char *compact_encoded_jwt, char **alg) {
1592 	char *input = NULL, *result = NULL;
1593 	char *p = strstr(compact_encoded_jwt ? compact_encoded_jwt : "", ".");
1594 	if (p == NULL) {
1595 		oidc_warn(r,
1596 				"could not parse first element separated by \".\" from input");
1597 		return NULL;
1598 	}
1599 	input = apr_pstrmemdup(r->pool, compact_encoded_jwt,
1600 			strlen(compact_encoded_jwt) - strlen(p));
1601 	if (oidc_base64url_decode(r->pool, &result, input) <= 0) {
1602 		oidc_warn(r, "oidc_base64url_decode returned an error");
1603 		return NULL;
1604 	}
1605 	if (alg) {
1606 		json_t *json = NULL;
1607 		oidc_util_decode_json_object(r, result, &json);
1608 		if (json)
1609 			*alg = apr_pstrdup(r->pool,
1610 					json_string_value(json_object_get(json, CJOSE_HDR_ALG)));
1611 		json_decref(json);
1612 	}
1613 	return result;
1614 }
1615 
1616 /*
1617  * check whether the provided string is a valid id_token and return its parsed contents
1618  */
oidc_proto_parse_idtoken(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * id_token,const char * nonce,oidc_jwt_t ** jwt,apr_byte_t is_code_flow)1619 apr_byte_t oidc_proto_parse_idtoken(request_rec *r, oidc_cfg *cfg,
1620 		oidc_provider_t *provider, const char *id_token, const char *nonce,
1621 		oidc_jwt_t **jwt, apr_byte_t is_code_flow) {
1622 
1623 	char *alg = NULL;
1624 	oidc_debug(r, "enter: id_token header=%s",
1625 			oidc_proto_peek_jwt_header(r, id_token, &alg));
1626 	apr_hash_t *decryption_keys = NULL;
1627 
1628 	char buf[APR_RFC822_DATE_LEN + 1];
1629 	oidc_jose_error_t err;
1630 	oidc_jwk_t *jwk = NULL;
1631 	if (oidc_util_create_symmetric_key(r, provider->client_secret,
1632 			oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256,
1633 			TRUE, &jwk) == FALSE)
1634 		return FALSE;
1635 
1636 	decryption_keys = oidc_util_merge_symmetric_key(r->pool, cfg->private_keys,
1637 			jwk);
1638 	if (provider->client_encryption_keys)
1639 		decryption_keys = oidc_util_merge_key_sets(r->pool, decryption_keys,
1640 				provider->client_encryption_keys);
1641 
1642 	if (oidc_jwt_parse(r->pool, id_token, jwt, decryption_keys, &err) == FALSE) {
1643 		oidc_error(r, "oidc_jwt_parse failed: %s", oidc_jose_e2s(r->pool, err));
1644 		oidc_jwt_destroy(*jwt);
1645 		*jwt = NULL;
1646 		return FALSE;
1647 	}
1648 
1649 	oidc_jwk_destroy(jwk);
1650 	oidc_debug(r,
1651 			"successfully parsed (and possibly decrypted) JWT with header=%s, and payload=%s",
1652 			(*jwt)->header.value.str, (*jwt)->payload.value.str);
1653 
1654 	// make signature validation exception for 'code' flow and the algorithm NONE
1655 	if (is_code_flow == FALSE || strcmp((*jwt)->header.alg, "none") != 0) {
1656 
1657 		jwk = NULL;
1658 		if (oidc_util_create_symmetric_key(r, provider->client_secret, 0,
1659 				NULL, TRUE, &jwk) == FALSE)
1660 			return FALSE;
1661 
1662 		oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
1663 				provider->jwks_refresh_interval, provider->ssl_validate_server };
1664 		if (oidc_proto_jwt_verify(r, cfg, *jwt, &jwks_uri,
1665 				oidc_util_merge_symmetric_key(r->pool, NULL, jwk),
1666 				provider->id_token_signed_response_alg) == FALSE) {
1667 
1668 			oidc_error(r,
1669 					"id_token signature could not be validated, aborting");
1670 			oidc_jwt_destroy(*jwt);
1671 			*jwt = NULL;
1672 			oidc_jwk_destroy(jwk);
1673 			return FALSE;
1674 		}
1675 		oidc_jwk_destroy(jwk);
1676 	}
1677 
1678 	/* this is where the meat is */
1679 	if (oidc_proto_validate_idtoken(r, provider, *jwt, nonce) == FALSE) {
1680 		oidc_error(r, "id_token payload could not be validated, aborting");
1681 		oidc_jwt_destroy(*jwt);
1682 		*jwt = NULL;
1683 		return FALSE;
1684 	}
1685 
1686 	/* log our results */
1687 
1688 	apr_rfc822_date(buf, apr_time_from_sec((*jwt)->payload.exp));
1689 	oidc_debug(r,
1690 			"valid id_token for user \"%s\" expires: [%s], in %ld secs from now)",
1691 			(*jwt)->payload.sub, buf,
1692 			(long)((*jwt)->payload.exp - apr_time_sec(apr_time_now())));
1693 
1694 	/* since we've made it so far, we may as well say it is a valid id_token */
1695 	return TRUE;
1696 }
1697 
1698 /*
1699  * check that the access_token type is supported
1700  */
oidc_proto_validate_token_type(request_rec * r,oidc_provider_t * provider,const char * token_type)1701 static apr_byte_t oidc_proto_validate_token_type(request_rec *r,
1702 		oidc_provider_t *provider, const char *token_type) {
1703 	/*  we only support bearer/Bearer  */
1704 	if ((token_type != NULL)
1705 			&& (apr_strnatcasecmp(token_type, OIDC_PROTO_BEARER) != 0)
1706 			&& (provider->userinfo_endpoint_url != NULL)) {
1707 		oidc_error(r,
1708 				"token_type is \"%s\" and UserInfo endpoint (%s) for issuer \"%s\" is set: can only deal with \"%s\" authentication against a UserInfo endpoint!",
1709 				token_type, provider->userinfo_endpoint_url, provider->issuer,
1710 				OIDC_PROTO_BEARER);
1711 		return FALSE;
1712 	}
1713 	return TRUE;
1714 }
1715 
1716 /*
1717  * setup for an endpoint call without authentication
1718  */
oidc_proto_endpoint_auth_none(request_rec * r,const char * client_id,apr_table_t * params)1719 static apr_byte_t oidc_proto_endpoint_auth_none(request_rec *r,
1720 		const char *client_id, apr_table_t *params) {
1721 	apr_table_set(params, OIDC_PROTO_CLIENT_ID, client_id);
1722 	return TRUE;
1723 }
1724 
1725 /*
1726  * setup for an endpoint call with OIDC client_secret_basic authentication
1727  */
oidc_proto_endpoint_client_secret_basic(request_rec * r,const char * client_id,const char * client_secret,char ** basic_auth_str)1728 static apr_byte_t oidc_proto_endpoint_client_secret_basic(request_rec *r,
1729 		const char *client_id, const char *client_secret, char **basic_auth_str) {
1730 	oidc_debug(r, "enter");
1731 	if (client_secret == NULL) {
1732 		oidc_error(r, "no client secret is configured");
1733 		return FALSE;
1734 	}
1735 	*basic_auth_str = apr_psprintf(r->pool, "%s:%s",
1736 			oidc_util_escape_string(r, client_id),
1737 			oidc_util_escape_string(r, client_secret));
1738 
1739 	return TRUE;
1740 }
1741 
1742 /*
1743  * setup for an endpoint call with OIDC client_secret_post authentication
1744  */
oidc_proto_endpoint_client_secret_post(request_rec * r,const char * client_id,const char * client_secret,apr_table_t * params)1745 static apr_byte_t oidc_proto_endpoint_client_secret_post(request_rec *r,
1746 		const char *client_id, const char *client_secret, apr_table_t *params) {
1747 	oidc_debug(r, "enter");
1748 	if (client_secret == NULL) {
1749 		oidc_error(r, "no client secret is configured");
1750 		return FALSE;
1751 	}
1752 	apr_table_set(params, OIDC_PROTO_CLIENT_ID, client_id);
1753 	apr_table_set(params, OIDC_PROTO_CLIENT_SECRET, client_secret);
1754 	return TRUE;
1755 }
1756 
1757 #define OIDC_PROTO_ASSERTION_JTI_LEN 16
1758 
1759 /*
1760  * helper function to create a JWT assertion for endpoint authentication
1761  */
oidc_proto_jwt_create(request_rec * r,const char * client_id,const char * audience,oidc_jwt_t ** out)1762 static apr_byte_t oidc_proto_jwt_create(request_rec *r, const char *client_id,
1763 		const char *audience, oidc_jwt_t **out) {
1764 
1765 	*out = oidc_jwt_new(r->pool, TRUE, TRUE);
1766 	oidc_jwt_t *jwt = *out;
1767 
1768 	char *jti = NULL;
1769 	oidc_proto_generate_random_string(r, &jti,
1770 			OIDC_PROTO_ASSERTION_JTI_LEN);
1771 
1772 	json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_ISS,
1773 			json_string(client_id));
1774 	json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_SUB,
1775 			json_string(client_id));
1776 	json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_AUD,
1777 			json_string(audience));
1778 	json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_JTI,
1779 			json_string(jti));
1780 	json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_EXP,
1781 			json_integer(apr_time_sec(apr_time_now()) + 60));
1782 	json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_IAT,
1783 			json_integer(apr_time_sec(apr_time_now())));
1784 
1785 	return TRUE;
1786 }
1787 
1788 /*
1789  * helper function to add a JWT assertion to the HTTP request as endpoint authentication
1790  */
oidc_proto_jwt_sign_and_add(request_rec * r,apr_table_t * params,oidc_jwt_t * jwt,oidc_jwk_t * jwk)1791 static apr_byte_t oidc_proto_jwt_sign_and_add(request_rec *r,
1792 		apr_table_t *params, oidc_jwt_t *jwt, oidc_jwk_t *jwk) {
1793 	oidc_jose_error_t err;
1794 
1795 	if (oidc_jwt_sign(r->pool, jwt, jwk, &err) == FALSE) {
1796 		oidc_error(r, "signing JWT failed: %s", oidc_jose_e2s(r->pool, err));
1797 		return FALSE;
1798 	}
1799 
1800 	char *cser = oidc_jwt_serialize(r->pool, jwt, &err);
1801 	if (cser == NULL) {
1802 		oidc_error(r, "oidc_jwt_serialize failed: %s",
1803 				oidc_jose_e2s(r->pool, err));
1804 		return FALSE;
1805 	}
1806 
1807 	apr_table_setn(params, OIDC_PROTO_CLIENT_ASSERTION_TYPE,
1808 			OIDC_PROTO_CLIENT_ASSERTION_TYPE_JWT_BEARER);
1809 	apr_table_set(params, OIDC_PROTO_CLIENT_ASSERTION, cser);
1810 
1811 	return TRUE;
1812 }
1813 
1814 #define OIDC_PROTO_JWT_ASSERTION_SYMMETRIC_ALG CJOSE_HDR_ALG_HS256
1815 
oidc_proto_endpoint_auth_client_secret_jwt(request_rec * r,const char * client_id,const char * client_secret,const char * audience,apr_table_t * params)1816 static apr_byte_t oidc_proto_endpoint_auth_client_secret_jwt(request_rec *r,
1817 		const char *client_id, const char *client_secret, const char *audience,
1818 		apr_table_t *params) {
1819 	oidc_jwt_t *jwt = NULL;
1820 	oidc_jose_error_t err;
1821 
1822 	oidc_debug(r, "enter");
1823 
1824 	if (oidc_proto_jwt_create(r, client_id, audience, &jwt) == FALSE)
1825 		return FALSE;
1826 
1827 	oidc_jwk_t *jwk = oidc_jwk_create_symmetric_key(r->pool, NULL,
1828 			(const unsigned char*) client_secret, strlen(client_secret),
1829 			FALSE, &err);
1830 	if (jwk == NULL) {
1831 		oidc_error(r, "parsing of client secret into JWK failed: %s",
1832 				oidc_jose_e2s(r->pool, err));
1833 		oidc_jwt_destroy(jwt);
1834 		return FALSE;
1835 	}
1836 
1837 	jwt->header.alg = apr_pstrdup(r->pool,
1838 			OIDC_PROTO_JWT_ASSERTION_SYMMETRIC_ALG);
1839 
1840 	oidc_proto_jwt_sign_and_add(r, params, jwt, jwk);
1841 
1842 	oidc_jwt_destroy(jwt);
1843 	oidc_jwk_destroy(jwk);
1844 
1845 	return TRUE;
1846 }
1847 
oidc_proto_endpoint_access_token_bearer(request_rec * r,oidc_cfg * cfg,const char * bearer_access_token,char ** bearer_auth_str)1848 static apr_byte_t oidc_proto_endpoint_access_token_bearer(request_rec *r,
1849 		oidc_cfg *cfg, const char *bearer_access_token, char **bearer_auth_str) {
1850 
1851 	apr_byte_t rv = TRUE;
1852 
1853 	if (bearer_access_token != NULL) {
1854 		*bearer_auth_str = apr_psprintf(r->pool, "%s", bearer_access_token);
1855 	} else {
1856 		oidc_error(r,
1857 				"endpoint auth method set to bearer access token but no token is provided");
1858 		rv = FALSE;
1859 	}
1860 
1861 	return rv;
1862 }
1863 
1864 #define OIDC_PROTO_JWT_ASSERTION_ASYMMETRIC_ALG CJOSE_HDR_ALG_RS256
1865 
oidc_proto_endpoint_auth_private_key_jwt(request_rec * r,oidc_cfg * cfg,const char * client_id,const apr_array_header_t * client_signing_keys,const char * audience,apr_table_t * params)1866 static apr_byte_t oidc_proto_endpoint_auth_private_key_jwt(request_rec *r,
1867 		oidc_cfg *cfg, const char *client_id,
1868 		const apr_array_header_t *client_signing_keys, const char *audience,
1869 		apr_table_t *params) {
1870 	oidc_jwt_t *jwt = NULL;
1871 	oidc_jwk_t *jwk = NULL;
1872 	const apr_array_header_t *signing_keys = NULL;
1873 
1874 	oidc_debug(r, "enter");
1875 
1876 	if (oidc_proto_jwt_create(r, client_id, audience, &jwt) == FALSE)
1877 		return FALSE;
1878 
1879 	if ((client_signing_keys != NULL) && (client_signing_keys->nelts > 0)) {
1880 		signing_keys = client_signing_keys;
1881 	} else if ((cfg->private_keys != NULL) && (cfg->private_keys->nelts > 0)) {
1882 		signing_keys = cfg->private_keys;
1883 	} else {
1884 		oidc_error(r,
1885 				"no private keys have been configured to use for private_key_jwt client authentication (" OIDCPrivateKeyFiles ")");
1886 		oidc_jwt_destroy(jwt);
1887 		return FALSE;
1888 	}
1889 
1890 	jwk = ((oidc_jwk_t**) signing_keys->elts)[0];
1891 
1892 	jwt->header.kid = apr_pstrdup(r->pool, jwk->kid);
1893 	jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_RS256);
1894 
1895 	oidc_proto_jwt_sign_and_add(r, params, jwt, jwk);
1896 
1897 	oidc_jwt_destroy(jwt);
1898 
1899 	return TRUE;
1900 }
1901 
oidc_proto_token_endpoint_auth(request_rec * r,oidc_cfg * cfg,const char * token_endpoint_auth,const char * client_id,const char * client_secret,const apr_array_header_t * client_signing_keys,const char * audience,apr_table_t * params,const char * bearer_access_token,char ** basic_auth_str,char ** bearer_auth_str)1902 apr_byte_t oidc_proto_token_endpoint_auth(request_rec *r, oidc_cfg *cfg,
1903 		const char *token_endpoint_auth, const char *client_id,
1904 		const char *client_secret,
1905 		const apr_array_header_t *client_signing_keys, const char *audience,
1906 		apr_table_t *params, const char *bearer_access_token,
1907 		char **basic_auth_str, char **bearer_auth_str) {
1908 
1909 	oidc_debug(r, "token_endpoint_auth=%s", token_endpoint_auth);
1910 
1911 	if (client_id == NULL) {
1912 		oidc_debug(r, "no client ID set: assume we don't need to authenticate");
1913 		return TRUE;
1914 	}
1915 
1916 	// default is client_secret_basic, but only if a client_secret is set,
1917 	// otherwise we are a public client
1918 	if ((token_endpoint_auth == NULL) && (client_secret != NULL))
1919 		token_endpoint_auth = OIDC_PROTO_CLIENT_SECRET_BASIC;
1920 
1921 	if ((token_endpoint_auth == NULL) || (apr_strnatcmp(token_endpoint_auth,
1922 			OIDC_PROTO_ENDPOINT_AUTH_NONE) == 0)) {
1923 		oidc_debug(r,
1924 				"no client secret is configured or the token endpoint auth method was set to \"%s\"; calling the token endpoint without client authentication; only public clients are supported",
1925 				OIDC_PROTO_ENDPOINT_AUTH_NONE);
1926 		return oidc_proto_endpoint_auth_none(r, client_id, params);
1927 	}
1928 
1929 	// if no client_secret is set and we don't authenticate using private_key_jwt,
1930 	// we can only be a public client since the other methods require a client_secret
1931 	if ((client_secret == NULL) && (apr_strnatcmp(token_endpoint_auth,
1932 			OIDC_PROTO_PRIVATE_KEY_JWT) != 0)) {
1933 		oidc_debug(r,
1934 				"no client secret set and not using private_key_jwt, assume we are a public client");
1935 		return oidc_proto_endpoint_auth_none(r, client_id, params);
1936 	}
1937 
1938 	if (apr_strnatcmp(token_endpoint_auth,
1939 			OIDC_PROTO_CLIENT_SECRET_BASIC) == 0)
1940 		return oidc_proto_endpoint_client_secret_basic(r, client_id,
1941 				client_secret, basic_auth_str);
1942 
1943 	if (apr_strnatcmp(token_endpoint_auth,
1944 			OIDC_PROTO_CLIENT_SECRET_POST) == 0)
1945 		return oidc_proto_endpoint_client_secret_post(r, client_id,
1946 				client_secret, params);
1947 
1948 	if (apr_strnatcmp(token_endpoint_auth,
1949 			OIDC_PROTO_CLIENT_SECRET_JWT) == 0)
1950 		return oidc_proto_endpoint_auth_client_secret_jwt(r, client_id,
1951 				client_secret, audience, params);
1952 
1953 	if (apr_strnatcmp(token_endpoint_auth,
1954 			OIDC_PROTO_PRIVATE_KEY_JWT) == 0)
1955 		return oidc_proto_endpoint_auth_private_key_jwt(r, cfg, client_id,
1956 				client_signing_keys, audience, params);
1957 
1958 	if (apr_strnatcmp(token_endpoint_auth,
1959 			OIDC_PROTO_BEARER_ACCESS_TOKEN) == 0) {
1960 		return oidc_proto_endpoint_access_token_bearer(r, cfg,
1961 				bearer_access_token, bearer_auth_str);
1962 	}
1963 
1964 	oidc_error(r, "uhm, shouldn't be here...");
1965 
1966 	return FALSE;
1967 }
1968 
1969 /*
1970  * send a code/refresh request to the token endpoint and return the parsed contents
1971  */
oidc_proto_token_endpoint_request(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,apr_table_t * params,char ** id_token,char ** access_token,char ** token_type,int * expires_in,char ** refresh_token)1972 static apr_byte_t oidc_proto_token_endpoint_request(request_rec *r,
1973 		oidc_cfg *cfg, oidc_provider_t *provider, apr_table_t *params,
1974 		char **id_token, char **access_token, char **token_type,
1975 		int *expires_in, char **refresh_token) {
1976 
1977 	char *response = NULL;
1978 	char *basic_auth = NULL;
1979 	char *bearer_auth = NULL;
1980 
1981 	/* add the token endpoint authentication credentials */
1982 	if (oidc_proto_token_endpoint_auth(r, cfg, provider->token_endpoint_auth,
1983 			provider->client_id, provider->client_secret,
1984 			provider->client_signing_keys, provider->token_endpoint_url, params,
1985 			NULL, &basic_auth, &bearer_auth) == FALSE)
1986 		return FALSE;
1987 
1988 	/* add any configured extra static parameters to the token endpoint */
1989 	oidc_util_table_add_query_encoded_params(r->pool, params,
1990 			provider->token_endpoint_params);
1991 
1992 	/* send the refresh request to the token endpoint */
1993 	if (oidc_util_http_post_form(r, provider->token_endpoint_url, params,
1994 			basic_auth, bearer_auth, provider->ssl_validate_server, &response,
1995 			cfg->http_timeout_long, cfg->outgoing_proxy,
1996 			oidc_dir_cfg_pass_cookies(r),
1997 			oidc_util_get_full_path(r->pool,
1998 					provider->token_endpoint_tls_client_cert),
1999 					oidc_util_get_full_path(r->pool,
2000 							provider->token_endpoint_tls_client_key)) == FALSE) {
2001 		oidc_warn(r, "error when calling the token endpoint (%s)",
2002 				provider->token_endpoint_url);
2003 		return FALSE;
2004 	}
2005 
2006 	/* check for errors, the response itself will have been logged already */
2007 	json_t *result = NULL;
2008 	if (oidc_util_decode_json_and_check_error(r, response, &result) == FALSE)
2009 		return FALSE;
2010 
2011 	/* get the id_token from the parsed response */
2012 	oidc_json_object_get_string(r->pool, result, OIDC_PROTO_ID_TOKEN, id_token,
2013 			NULL);
2014 
2015 	/* get the access_token from the parsed response */
2016 	oidc_json_object_get_string(r->pool, result, OIDC_PROTO_ACCESS_TOKEN,
2017 			access_token,
2018 			NULL);
2019 
2020 	/* get the token type from the parsed response */
2021 	oidc_json_object_get_string(r->pool, result, OIDC_PROTO_TOKEN_TYPE,
2022 			token_type,
2023 			NULL);
2024 
2025 	/* check the new token type */
2026 	if (token_type != NULL) {
2027 		if (oidc_proto_validate_token_type(r, provider, *token_type) == FALSE) {
2028 			oidc_warn(r, "access token type did not validate, dropping it");
2029 			*access_token = NULL;
2030 		}
2031 	}
2032 
2033 	/* get the expires_in value */
2034 	oidc_json_object_get_int(r->pool, result, OIDC_PROTO_EXPIRES_IN, expires_in,
2035 			-1);
2036 
2037 	/* get the refresh_token from the parsed response */
2038 	oidc_json_object_get_string(r->pool, result, OIDC_PROTO_REFRESH_TOKEN,
2039 			refresh_token,
2040 			NULL);
2041 
2042 	json_decref(result);
2043 
2044 	return TRUE;
2045 }
2046 
2047 /*
2048  * resolves the code received from the OP in to an id_token, access_token and refresh_token
2049  */
oidc_proto_resolve_code(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * code,const char * code_verifier,char ** id_token,char ** access_token,char ** token_type,int * expires_in,char ** refresh_token,const char * state)2050 static apr_byte_t oidc_proto_resolve_code(request_rec *r, oidc_cfg *cfg,
2051 		oidc_provider_t *provider, const char *code, const char *code_verifier,
2052 		char **id_token, char **access_token, char **token_type,
2053 		int *expires_in, char **refresh_token, const char *state) {
2054 
2055 	oidc_debug(r, "enter");
2056 
2057 	/* assemble the parameters for a call to the token endpoint */
2058 	apr_table_t *params = apr_table_make(r->pool, 5);
2059 	apr_table_setn(params, OIDC_PROTO_GRANT_TYPE,
2060 			OIDC_PROTO_GRANT_TYPE_AUTHZ_CODE);
2061 	apr_table_setn(params, OIDC_PROTO_CODE, code);
2062 	apr_table_set(params, OIDC_PROTO_REDIRECT_URI,
2063 			oidc_get_redirect_uri_iss(r, cfg, provider));
2064 
2065 	if (code_verifier)
2066 		apr_table_setn(params, OIDC_PROTO_CODE_VERIFIER, code_verifier);
2067 
2068 	/* add state to mitigate IDP mixup attacks, only useful in a multi-provider setup */
2069 	if ((cfg->metadata_dir != NULL) && (state))
2070 		apr_table_setn(params, OIDC_PROTO_STATE, state);
2071 
2072 	return oidc_proto_token_endpoint_request(r, cfg, provider, params, id_token,
2073 			access_token, token_type, expires_in, refresh_token);
2074 }
2075 
2076 /*
2077  * refreshes the access_token/id_token /refresh_token received from the OP using the refresh_token
2078  */
oidc_proto_refresh_request(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * rtoken,char ** id_token,char ** access_token,char ** token_type,int * expires_in,char ** refresh_token)2079 apr_byte_t oidc_proto_refresh_request(request_rec *r, oidc_cfg *cfg,
2080 		oidc_provider_t *provider, const char *rtoken, char **id_token,
2081 		char **access_token, char **token_type, int *expires_in,
2082 		char **refresh_token) {
2083 
2084 	oidc_debug(r, "enter");
2085 
2086 	/* assemble the parameters for a call to the token endpoint */
2087 	apr_table_t *params = apr_table_make(r->pool, 5);
2088 	apr_table_setn(params, OIDC_PROTO_GRANT_TYPE,
2089 			OIDC_PROTO_GRANT_TYPE_REFRESH_TOKEN);
2090 	apr_table_setn(params, OIDC_PROTO_REFRESH_TOKEN, rtoken);
2091 	apr_table_setn(params, OIDC_PROTO_SCOPE, provider->scope);
2092 
2093 	return oidc_proto_token_endpoint_request(r, cfg, provider, params, id_token,
2094 			access_token, token_type, expires_in, refresh_token);
2095 }
2096 
oidc_user_info_response_validate(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,char ** response,json_t ** claims,char ** userinfo_jwt)2097 static apr_byte_t oidc_user_info_response_validate(request_rec *r,
2098 		oidc_cfg *cfg, oidc_provider_t *provider, char **response,
2099 		json_t **claims, char **userinfo_jwt) {
2100 
2101 	oidc_debug(r,
2102 			"enter: userinfo_signed_response_alg=%s, userinfo_encrypted_response_alg=%s, userinfo_encrypted_response_enc=%s",
2103 			provider->userinfo_signed_response_alg,
2104 			provider->userinfo_encrypted_response_alg,
2105 			provider->userinfo_encrypted_response_enc);
2106 
2107 	char *alg = NULL;
2108 	if ((provider->userinfo_signed_response_alg != NULL)
2109 			|| (provider->userinfo_encrypted_response_alg != NULL)
2110 			|| (provider->userinfo_encrypted_response_enc != NULL)) {
2111 		oidc_debug(r, "JWT header=%s",
2112 				oidc_proto_peek_jwt_header(r, *response, &alg));
2113 	}
2114 
2115 	oidc_jose_error_t err;
2116 	oidc_jwk_t *jwk = NULL;
2117 	oidc_jwt_t *jwt = NULL;
2118 	char *payload = NULL;
2119 
2120 	if (oidc_util_create_symmetric_key(r, provider->client_secret,
2121 			oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256,
2122 			TRUE, &jwk) == FALSE)
2123 		return FALSE;
2124 
2125 	if (provider->userinfo_encrypted_response_alg != NULL) {
2126 		if (oidc_jwe_decrypt(r->pool, *response,
2127 				oidc_util_merge_symmetric_key(r->pool, cfg->private_keys, jwk),
2128 				&payload, &err, TRUE) == FALSE) {
2129 			oidc_error(r, "oidc_jwe_decrypt failed: %s",
2130 					oidc_jose_e2s(r->pool, err));
2131 			oidc_jwk_destroy(jwk);
2132 			return FALSE;
2133 		} else {
2134 			oidc_debug(r,
2135 					"successfully decrypted JWE returned from userinfo endpoint: %s",
2136 					payload);
2137 			*response = payload;
2138 		}
2139 	}
2140 
2141 	if (provider->userinfo_signed_response_alg != NULL) {
2142 		if (oidc_jwt_parse(r->pool, *response, &jwt,
2143 				oidc_util_merge_symmetric_key(r->pool, cfg->private_keys, jwk),
2144 				&err) == FALSE) {
2145 			oidc_error(r, "oidc_jwt_parse failed: %s",
2146 					oidc_jose_e2s(r->pool, err));
2147 			oidc_jwt_destroy(jwt);
2148 			oidc_jwk_destroy(jwk);
2149 			return FALSE;
2150 		}
2151 		oidc_debug(r, "successfully parsed JWT with header=%s, and payload=%s",
2152 				jwt->header.value.str, jwt->payload.value.str);
2153 
2154 		oidc_jwk_destroy(jwk);
2155 
2156 		jwk = NULL;
2157 		if (oidc_util_create_symmetric_key(r, provider->client_secret, 0,
2158 				NULL, TRUE, &jwk) == FALSE)
2159 			return FALSE;
2160 
2161 		oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
2162 				provider->jwks_refresh_interval, provider->ssl_validate_server };
2163 		if (oidc_proto_jwt_verify(r, cfg, jwt, &jwks_uri,
2164 				oidc_util_merge_symmetric_key(r->pool, NULL, jwk),
2165 				provider->userinfo_signed_response_alg) == FALSE) {
2166 
2167 			oidc_error(r, "JWT signature could not be validated, aborting");
2168 			oidc_jwt_destroy(jwt);
2169 			oidc_jwk_destroy(jwk);
2170 			return FALSE;
2171 		}
2172 		oidc_jwk_destroy(jwk);
2173 		oidc_debug(r,
2174 				"successfully verified signed JWT returned from userinfo endpoint: %s",
2175 				jwt->payload.value.str);
2176 
2177 		*userinfo_jwt = apr_pstrdup(r->pool, *response);
2178 		*claims = json_deep_copy(jwt->payload.value.json);
2179 		*response = apr_pstrdup(r->pool, jwt->payload.value.str);
2180 		oidc_jwt_destroy(jwt);
2181 
2182 		return TRUE;
2183 	}
2184 
2185 	oidc_jwk_destroy(jwk);
2186 
2187 	return oidc_util_decode_json_and_check_error(r, *response, claims);
2188 }
2189 
2190 #define OIDC_COMPOSITE_CLAIM_NAMES        "_claim_names"
2191 #define OIDC_COMPOSITE_CLAIM_SOURCES      "_claim_sources"
2192 #define OIDC_COMPOSITE_CLAIM_JWT          "JWT"
2193 #define OIDC_COMPOSITE_CLAIM_ACCESS_TOKEN OIDC_PROTO_ACCESS_TOKEN
2194 #define OIDC_COMPOSITE_CLAIM_ENDPOINT     "endpoint"
2195 
oidc_proto_resolve_composite_claims(request_rec * r,oidc_cfg * cfg,json_t * claims)2196 static apr_byte_t oidc_proto_resolve_composite_claims(request_rec *r,
2197 		oidc_cfg *cfg, json_t *claims) {
2198 	const char *key;
2199 	json_t *value;
2200 	void *iter;
2201 	json_t *sources, *names, *decoded;
2202 	oidc_jose_error_t err;
2203 	oidc_jwk_t *jwk = NULL;
2204 
2205 	oidc_debug(r, "enter");
2206 
2207 	names = json_object_get(claims, OIDC_COMPOSITE_CLAIM_NAMES);
2208 	if ((names == NULL) || (!json_is_object(names)))
2209 		return FALSE;
2210 
2211 	sources = json_object_get(claims, OIDC_COMPOSITE_CLAIM_SOURCES);
2212 	if ((sources == NULL) || (!json_is_object(sources))) {
2213 		oidc_debug(r, "%s found, but no %s found", OIDC_COMPOSITE_CLAIM_NAMES,
2214 				OIDC_COMPOSITE_CLAIM_SOURCES);
2215 		return FALSE;
2216 	}
2217 
2218 	decoded = json_object();
2219 
2220 	iter = json_object_iter(sources);
2221 	while (iter) {
2222 		key = json_object_iter_key(iter);
2223 		value = json_object_iter_value(iter);
2224 		if ((value != NULL) && (json_is_object(value))) {
2225 			json_t *jwt = json_object_get(value, OIDC_COMPOSITE_CLAIM_JWT);
2226 			char *s_json = NULL;
2227 			if ((jwt != NULL) && (json_is_string(jwt))) {
2228 				s_json = apr_pstrdup(r->pool, json_string_value(jwt));
2229 			} else {
2230 				const char *access_token = json_string_value(
2231 						json_object_get(value,
2232 								OIDC_COMPOSITE_CLAIM_ACCESS_TOKEN));
2233 				const char *endpoint = json_string_value(json_object_get(value,
2234 						OIDC_COMPOSITE_CLAIM_ENDPOINT));
2235 				if ((access_token != NULL) && (endpoint != NULL)) {
2236 					oidc_util_http_get(r, endpoint,
2237 							NULL, NULL, access_token, cfg->provider.ssl_validate_server,
2238 							&s_json, cfg->http_timeout_long,
2239 							cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r),
2240 							NULL, NULL);
2241 				}
2242 			}
2243 			if ((s_json != NULL) && (strcmp(s_json, "") != 0)) {
2244 				oidc_jwt_t *jwt = NULL;
2245 				if (oidc_jwt_parse(r->pool, s_json, &jwt,
2246 						oidc_util_merge_symmetric_key(r->pool,
2247 								cfg->private_keys, jwk), &err) == FALSE) {
2248 					oidc_error(r,
2249 							"could not parse JWT from aggregated claim \"%s\": %s",
2250 							key, oidc_jose_e2s(r->pool, err));
2251 				} else {
2252 					json_t *v = json_object_get(decoded, key);
2253 					if (v == NULL) {
2254 						v = json_object();
2255 						json_object_set_new(decoded, key, v);
2256 					}
2257 					oidc_util_json_merge(r, jwt->payload.value.json, v);
2258 				}
2259 				oidc_jwt_destroy(jwt);
2260 			}
2261 		}
2262 		iter = json_object_iter_next(sources, iter);
2263 	}
2264 
2265 	iter = json_object_iter(names);
2266 	while (iter) {
2267 		key = json_object_iter_key(iter);
2268 		const char *s_value = json_string_value(json_object_iter_value(iter));
2269 		if (s_value != NULL) {
2270 			oidc_debug(r, "processing: %s: %s", key, s_value);
2271 			json_t *values = json_object_get(decoded, s_value);
2272 			if (values != NULL) {
2273 				json_object_set(claims, key, json_object_get(values, key));
2274 			} else {
2275 				oidc_warn(r, "no values for source \"%s\" found", s_value);
2276 			}
2277 		} else {
2278 			oidc_warn(r, "no string value found for claim \"%s\"", key);
2279 		}
2280 		iter = json_object_iter_next(names, iter);
2281 	}
2282 
2283 	json_object_del(claims, OIDC_COMPOSITE_CLAIM_NAMES);
2284 	json_object_del(claims, OIDC_COMPOSITE_CLAIM_SOURCES);
2285 	json_decref(decoded);
2286 
2287 	return TRUE;
2288 }
2289 
2290 /*
2291  * get claims from the OP UserInfo endpoint using the provided access_token
2292  */
oidc_proto_resolve_userinfo(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,const char * id_token_sub,const char * access_token,char ** response,char ** userinfo_jwt)2293 apr_byte_t oidc_proto_resolve_userinfo(request_rec *r, oidc_cfg *cfg,
2294 		oidc_provider_t *provider, const char *id_token_sub,
2295 		const char *access_token, char **response, char **userinfo_jwt) {
2296 
2297 	oidc_debug(r, "enter, endpoint=%s, access_token=%s",
2298 			provider->userinfo_endpoint_url, access_token);
2299 
2300 	/* get the JSON response */
2301 	if (provider->userinfo_token_method == OIDC_USER_INFO_TOKEN_METHOD_HEADER) {
2302 		if (oidc_util_http_get(r, provider->userinfo_endpoint_url,
2303 				NULL, NULL, access_token, provider->ssl_validate_server, response,
2304 				cfg->http_timeout_long, cfg->outgoing_proxy,
2305 				oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE)
2306 			return FALSE;
2307 	} else if (provider->userinfo_token_method
2308 			== OIDC_USER_INFO_TOKEN_METHOD_POST) {
2309 		apr_table_t *params = apr_table_make(r->pool, 4);
2310 		apr_table_setn(params, OIDC_PROTO_ACCESS_TOKEN, access_token);
2311 		if (oidc_util_http_post_form(r, provider->userinfo_endpoint_url, params,
2312 				NULL, NULL, provider->ssl_validate_server, response,
2313 				cfg->http_timeout_long, cfg->outgoing_proxy,
2314 				oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE)
2315 			return FALSE;
2316 	} else {
2317 		oidc_error(r, "unsupported userinfo token presentation method: %d",
2318 				provider->userinfo_token_method);
2319 		return FALSE;
2320 	}
2321 
2322 	json_t *claims = NULL;
2323 	if (oidc_user_info_response_validate(r, cfg, provider, response, &claims,
2324 			userinfo_jwt) == FALSE)
2325 		return FALSE;
2326 
2327 	if (oidc_proto_resolve_composite_claims(r, cfg, claims) == TRUE)
2328 		*response = oidc_util_encode_json_object(r, claims,
2329 				JSON_PRESERVE_ORDER | JSON_COMPACT);
2330 
2331 	char *user_info_sub = NULL;
2332 	oidc_jose_get_string(r->pool, claims, OIDC_CLAIM_SUB, FALSE, &user_info_sub,
2333 			NULL);
2334 
2335 	oidc_debug(r, "id_token_sub=%s, user_info_sub=%s", id_token_sub,
2336 			user_info_sub);
2337 
2338 	if ((user_info_sub == NULL)
2339 			&& (apr_table_get(r->subprocess_env, "OIDC_NO_USERINFO_SUB") == NULL)) {
2340 		oidc_error(r,
2341 				"mandatory claim (\"%s\") was not returned from userinfo endpoint (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse)",
2342 				OIDC_CLAIM_SUB);
2343 		json_decref(claims);
2344 		return FALSE;
2345 	}
2346 
2347 	if ((id_token_sub != NULL) && (user_info_sub != NULL)) {
2348 		if (apr_strnatcmp(id_token_sub, user_info_sub) != 0) {
2349 			oidc_error(r,
2350 					"\"%s\" claim (\"%s\") returned from userinfo endpoint does not match the one in the id_token (\"%s\")",
2351 					OIDC_CLAIM_SUB, user_info_sub, id_token_sub);
2352 			json_decref(claims);
2353 			return FALSE;
2354 		}
2355 	}
2356 
2357 	json_decref(claims);
2358 
2359 	return TRUE;
2360 }
2361 
2362 /*
2363  * based on a resource perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
2364  */
oidc_proto_webfinger_discovery(request_rec * r,oidc_cfg * cfg,const char * resource,const char * domain,char ** issuer)2365 static apr_byte_t oidc_proto_webfinger_discovery(request_rec *r, oidc_cfg *cfg,
2366 		const char *resource, const char *domain, char **issuer) {
2367 
2368 	const char *url = apr_psprintf(r->pool, "https://%s/.well-known/webfinger",
2369 			domain);
2370 
2371 	apr_table_t *params = apr_table_make(r->pool, 1);
2372 	apr_table_setn(params, "resource", resource);
2373 	apr_table_setn(params, "rel", "http://openid.net/specs/connect/1.0/issuer");
2374 
2375 	char *response = NULL;
2376 	if (oidc_util_http_get(r, url, params, NULL, NULL,
2377 			cfg->provider.ssl_validate_server, &response,
2378 			cfg->http_timeout_short, cfg->outgoing_proxy,
2379 			oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE) {
2380 		/* errors will have been logged by now */
2381 		return FALSE;
2382 	}
2383 
2384 	/* decode and see if it is not an error response somehow */
2385 	json_t *j_response = NULL;
2386 	if (oidc_util_decode_json_and_check_error(r, response, &j_response) == FALSE)
2387 		return FALSE;
2388 
2389 	/* get the links parameter */
2390 	json_t *j_links = json_object_get(j_response, "links");
2391 	if ((j_links == NULL) || (!json_is_array(j_links))) {
2392 		oidc_error(r, "response JSON object did not contain a \"links\" array");
2393 		json_decref(j_response);
2394 		return FALSE;
2395 	}
2396 
2397 	/* get the one-and-only object in the "links" array */
2398 	json_t *j_object = json_array_get(j_links, 0);
2399 	if ((j_object == NULL) || (!json_is_object(j_object))) {
2400 		oidc_error(r,
2401 				"response JSON object did not contain a JSON object as the first element in the \"links\" array");
2402 		json_decref(j_response);
2403 		return FALSE;
2404 	}
2405 
2406 	/* get the href from that object, which is the issuer value */
2407 	json_t *j_href = json_object_get(j_object, "href");
2408 	if ((j_href == NULL) || (!json_is_string(j_href))) {
2409 		oidc_error(r,
2410 				"response JSON object did not contain a \"href\" element in the first \"links\" array object");
2411 		json_decref(j_response);
2412 		return FALSE;
2413 	}
2414 
2415 	/* check that the link is on secure HTTPs */
2416 	if (oidc_valid_url(r->pool, json_string_value(j_href), "https") != NULL) {
2417 		oidc_error(r,
2418 				"response JSON object contains an \"href\" value that is not a valid \"https\" URL: %s",
2419 				json_string_value(j_href));
2420 		json_decref(j_response);
2421 		return FALSE;
2422 	}
2423 
2424 	*issuer = apr_pstrdup(r->pool, json_string_value(j_href));
2425 
2426 	oidc_debug(r,
2427 			"returning issuer \"%s\" for resource \"%s\" after doing successful webfinger-based discovery",
2428 			*issuer, resource);
2429 
2430 	json_decref(j_response);
2431 
2432 	return TRUE;
2433 }
2434 
2435 /*
2436  * based on an account name, perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
2437  */
oidc_proto_account_based_discovery(request_rec * r,oidc_cfg * cfg,const char * acct,char ** issuer)2438 apr_byte_t oidc_proto_account_based_discovery(request_rec *r, oidc_cfg *cfg,
2439 		const char *acct, char **issuer) {
2440 
2441 	// TODO: maybe show intermediate/progress screen "discovering..."
2442 
2443 	oidc_debug(r, "enter, acct=%s", acct);
2444 
2445 	const char *resource = apr_psprintf(r->pool, "acct:%s", acct);
2446 	const char *domain = strrchr(acct, OIDC_CHAR_AT);
2447 	if (domain == NULL) {
2448 		oidc_error(r, "invalid account name");
2449 		return FALSE;
2450 	}
2451 	domain++;
2452 
2453 	return oidc_proto_webfinger_discovery(r, cfg, resource, domain, issuer);
2454 }
2455 
2456 /*
2457  * based on user identifier URL, perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
2458  */
oidc_proto_url_based_discovery(request_rec * r,oidc_cfg * cfg,const char * url,char ** issuer)2459 apr_byte_t oidc_proto_url_based_discovery(request_rec *r, oidc_cfg *cfg,
2460 		const char *url, char **issuer) {
2461 
2462 	oidc_debug(r, "enter, url=%s", url);
2463 
2464 	apr_uri_t uri;
2465 	apr_uri_parse(r->pool, url, &uri);
2466 
2467 	char *domain = uri.hostname;
2468 	if (uri.port_str != NULL)
2469 		domain = apr_psprintf(r->pool, "%s:%s", domain, uri.port_str);
2470 
2471 	return oidc_proto_webfinger_discovery(r, cfg, url, domain, issuer);
2472 }
2473 
oidc_proto_javascript_implicit(request_rec * r,oidc_cfg * c)2474 int oidc_proto_javascript_implicit(request_rec *r, oidc_cfg *c) {
2475 
2476 	oidc_debug(r, "enter");
2477 
2478 	const char *java_script =
2479 			"    <script type=\"text/javascript\">\n"
2480 			"      function postOnLoad() {\n"
2481 			"        encoded = location.hash.substring(1).split('&');\n"
2482 			"        for (i = 0; i < encoded.length; i++) {\n"
2483 			"          encoded[i].replace(/\\+/g, ' ');\n"
2484 			"          var n = encoded[i].indexOf('=');\n"
2485 			"          var input = document.createElement('input');\n"
2486 			"          input.type = 'hidden';\n"
2487 			"          input.name = decodeURIComponent(encoded[i].substring(0, n));\n"
2488 			"          input.value = decodeURIComponent(encoded[i].substring(n+1));\n"
2489 			"          document.forms[0].appendChild(input);\n"
2490 			"        }\n"
2491 			"        document.forms[0].action = window.location.href.substr(0, window.location.href.indexOf('#'));\n"
2492 			"        document.forms[0].submit();\n"
2493 			"      }\n"
2494 			"    </script>\n";
2495 
2496 	const char *html_body =
2497 			"    <p>Submitting...</p>\n"
2498 			"    <form method=\"post\" action=\"\">\n"
2499 			"      <p>\n"
2500 			"        <input type=\"hidden\" name=\"" OIDC_PROTO_RESPONSE_MODE "\" value=\"" OIDC_PROTO_RESPONSE_MODE_FRAGMENT "\">\n"
2501 			"      </p>\n"
2502 			"    </form>\n";
2503 
2504 	return oidc_util_html_send(r, "Submitting...", java_script, "postOnLoad",
2505 			html_body, OK);
2506 }
2507 
2508 /*
2509  * check a provided hash value (at_hash|c_hash) against a corresponding hash calculated for a specified value and algorithm
2510  */
oidc_proto_validate_hash(request_rec * r,const char * alg,const char * hash,const char * value,const char * type)2511 static apr_byte_t oidc_proto_validate_hash(request_rec *r, const char *alg,
2512 		const char *hash, const char *value, const char *type) {
2513 
2514 	char *calc = NULL;
2515 	unsigned int calc_len = 0;
2516 	unsigned int hash_len = oidc_jose_hash_length(alg) / 2;
2517 	oidc_jose_error_t err;
2518 
2519 	/* hash the provided access_token */
2520 	if (oidc_jose_hash_string(r->pool, alg, value, &calc, &calc_len,
2521 			&err) == FALSE) {
2522 		oidc_error(r, "oidc_jose_hash_string failed: %s",
2523 				oidc_jose_e2s(r->pool, err));
2524 		return FALSE;
2525 	}
2526 
2527 	/* calculate the base64url-encoded value of the hash */
2528 	char *decoded = NULL;
2529 	unsigned int decoded_len = oidc_base64url_decode(r->pool, &decoded, hash);
2530 	if (decoded_len <= 0) {
2531 		oidc_error(r, "oidc_base64url_decode returned an error");
2532 		return FALSE;
2533 	}
2534 
2535 	oidc_debug(r, "hash_len=%d, decoded_len=%d, calc_len=%d", hash_len,
2536 			decoded_len, calc_len);
2537 
2538 	/* compare the calculated hash against the provided hash */
2539 	if ((decoded_len < hash_len) || (calc_len < hash_len)
2540 			|| (memcmp(decoded, calc, hash_len) != 0)) {
2541 		oidc_error(r,
2542 				"provided \"%s\" hash value (%s) does not match the calculated value",
2543 				type, hash);
2544 		return FALSE;
2545 	}
2546 
2547 	oidc_debug(r,
2548 			"successfully validated the provided \"%s\" hash value (%s) against the calculated value",
2549 			type, hash);
2550 
2551 	return TRUE;
2552 }
2553 
2554 /*
2555  * check a hash value in the id_token against the corresponding hash calculated over a provided value
2556  */
oidc_proto_validate_hash_value(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * response_type,const char * value,const char * key,apr_array_header_t * required_for_flows)2557 static apr_byte_t oidc_proto_validate_hash_value(request_rec *r,
2558 		oidc_provider_t *provider, oidc_jwt_t *jwt, const char *response_type,
2559 		const char *value, const char *key,
2560 		apr_array_header_t *required_for_flows) {
2561 
2562 	/*
2563 	 * get the hash value from the id_token
2564 	 */
2565 	char *hash = NULL;
2566 	oidc_jose_get_string(r->pool, jwt->payload.value.json, key, FALSE, &hash,
2567 			NULL);
2568 
2569 	/*
2570 	 * check if the hash was present
2571 	 */
2572 	if (hash == NULL) {
2573 
2574 		/* no hash..., now see if the flow required it */
2575 		int i;
2576 		for (i = 0; i < required_for_flows->nelts; i++) {
2577 			if (oidc_util_spaced_string_equals(r->pool, response_type,
2578 					((const char**) required_for_flows->elts)[i])) {
2579 				oidc_warn(r, "flow is \"%s\", but no %s found in id_token",
2580 						response_type, key);
2581 				return FALSE;
2582 			}
2583 		}
2584 
2585 		/* no hash but it was not required anyway */
2586 		return TRUE;
2587 	}
2588 
2589 	/*
2590 	 * we have a hash, validate it and return the result
2591 	 */
2592 	return oidc_proto_validate_hash(r, jwt->header.alg, hash, value, key);
2593 }
2594 
2595 /*
2596  * check the c_hash value in the id_token against the code
2597  */
oidc_proto_validate_code(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * response_type,const char * code)2598 apr_byte_t oidc_proto_validate_code(request_rec *r, oidc_provider_t *provider,
2599 		oidc_jwt_t *jwt, const char *response_type, const char *code) {
2600 	apr_array_header_t *required_for_flows = apr_array_make(r->pool, 2,
2601 			sizeof(const char*));
2602 	*(const char**) apr_array_push(required_for_flows) =
2603 			OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN;
2604 	*(const char**) apr_array_push(required_for_flows) =
2605 			OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
2606 	if (oidc_proto_validate_hash_value(r, provider, jwt, response_type, code,
2607 			OIDC_CLAIM_C_HASH, required_for_flows) == FALSE) {
2608 		oidc_error(r, "could not validate code against \"%s\" claim value",
2609 				OIDC_CLAIM_C_HASH);
2610 		return FALSE;
2611 	}
2612 	return TRUE;
2613 }
2614 
2615 /*
2616  * check the at_hash value in the id_token against the access_token
2617  */
oidc_proto_validate_access_token(request_rec * r,oidc_provider_t * provider,oidc_jwt_t * jwt,const char * response_type,const char * access_token)2618 apr_byte_t oidc_proto_validate_access_token(request_rec *r,
2619 		oidc_provider_t *provider, oidc_jwt_t *jwt, const char *response_type,
2620 		const char *access_token) {
2621 	apr_array_header_t *required_for_flows = apr_array_make(r->pool, 2,
2622 			sizeof(const char*));
2623 	*(const char**) apr_array_push(required_for_flows) =
2624 			OIDC_PROTO_RESPONSE_TYPE_IDTOKEN_TOKEN;
2625 	*(const char**) apr_array_push(required_for_flows) =
2626 			OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
2627 	if (oidc_proto_validate_hash_value(r, provider, jwt, response_type,
2628 			access_token, OIDC_CLAIM_AT_HASH, required_for_flows) == FALSE) {
2629 		oidc_error(r,
2630 				"could not validate access token against \"%s\" claim value",
2631 				OIDC_CLAIM_AT_HASH);
2632 		return FALSE;
2633 	}
2634 	return TRUE;
2635 }
2636 
2637 /*
2638  * return the supported flows
2639  */
oidc_proto_supported_flows(apr_pool_t * pool)2640 apr_array_header_t* oidc_proto_supported_flows(apr_pool_t *pool) {
2641 	apr_array_header_t *result = apr_array_make(pool, 6, sizeof(const char*));
2642 	*(const char**) apr_array_push(result) = OIDC_PROTO_RESPONSE_TYPE_CODE;
2643 	*(const char**) apr_array_push(result) =
2644 			OIDC_PROTO_RESPONSE_TYPE_IDTOKEN;
2645 	*(const char**) apr_array_push(result) =
2646 			OIDC_PROTO_RESPONSE_TYPE_IDTOKEN_TOKEN;
2647 	*(const char**) apr_array_push(result) =
2648 			OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN;
2649 	*(const char**) apr_array_push(result) =
2650 			OIDC_PROTO_RESPONSE_TYPE_CODE_TOKEN;
2651 	*(const char**) apr_array_push(result) =
2652 			OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
2653 	return result;
2654 }
2655 
2656 /*
2657  * check if a particular OpenID Connect flow is supported
2658  */
oidc_proto_flow_is_supported(apr_pool_t * pool,const char * flow)2659 apr_byte_t oidc_proto_flow_is_supported(apr_pool_t *pool, const char *flow) {
2660 	apr_array_header_t *flows = oidc_proto_supported_flows(pool);
2661 	int i;
2662 	for (i = 0; i < flows->nelts; i++) {
2663 		if (oidc_util_spaced_string_equals(pool, flow,
2664 				((const char**) flows->elts)[i]))
2665 			return TRUE;
2666 	}
2667 	return FALSE;
2668 }
2669 
2670 /*
2671  * check the required parameters for the various flows after resolving the authorization code
2672  */
oidc_proto_validate_code_response(request_rec * r,const char * response_type,char * id_token,char * access_token,char * token_type)2673 static apr_byte_t oidc_proto_validate_code_response(request_rec *r,
2674 		const char *response_type, char *id_token, char *access_token,
2675 		char *token_type) {
2676 
2677 	oidc_debug(r, "enter");
2678 
2679 	/*
2680 	 * check id_token parameter
2681 	 */
2682 	if (!oidc_util_spaced_string_contains(r->pool, response_type,
2683 			OIDC_PROTO_RESPONSE_TYPE_IDTOKEN)) {
2684 		if (id_token == NULL) {
2685 			oidc_error(r,
2686 					"requested flow is \"%s\" but no \"%s\" parameter found in the code response",
2687 					response_type, OIDC_PROTO_ID_TOKEN);
2688 			return FALSE;
2689 		}
2690 	} else {
2691 		if (id_token != NULL) {
2692 			oidc_warn(r,
2693 					"requested flow is \"%s\" but there is an \"%s\" parameter in the code response that will be dropped",
2694 					response_type, OIDC_PROTO_ID_TOKEN);
2695 		}
2696 	}
2697 
2698 	/*
2699 	 * check access_token parameter
2700 	 */
2701 	if (!oidc_util_spaced_string_contains(r->pool, response_type,
2702 			OIDC_PROTO_RESPONSE_TYPE_TOKEN)) {
2703 		if (access_token == NULL) {
2704 			oidc_error(r,
2705 					"requested flow is \"%s\" but no \"%s\" parameter found in the code response",
2706 					response_type, OIDC_PROTO_ACCESS_TOKEN);
2707 			return FALSE;
2708 		}
2709 		if (token_type == NULL) {
2710 			oidc_error(r,
2711 					"requested flow is \"%s\" but no \"%s\" parameter found in the code response",
2712 					response_type, OIDC_PROTO_TOKEN_TYPE);
2713 			return FALSE;
2714 		}
2715 	} else {
2716 		if (access_token != NULL) {
2717 			oidc_warn(r,
2718 					"requested flow is \"%s\" but there is an \"%s\" parameter in the code response that will be dropped",
2719 					response_type, OIDC_PROTO_ACCESS_TOKEN);
2720 		}
2721 
2722 		if (token_type != NULL) {
2723 			oidc_warn(r,
2724 					"requested flow is \"%s\" but there is a \"%s\" parameter in the code response that will be dropped",
2725 					response_type, OIDC_PROTO_TOKEN_TYPE);
2726 		}
2727 	}
2728 
2729 	return TRUE;
2730 }
2731 
2732 /*
2733  * validate the response parameters provided by the OP against the requested response type
2734  */
oidc_proto_validate_response_type(request_rec * r,const char * requested_response_type,const char * code,const char * id_token,const char * access_token)2735 static apr_byte_t oidc_proto_validate_response_type(request_rec *r,
2736 		const char *requested_response_type, const char *code,
2737 		const char *id_token, const char *access_token) {
2738 
2739 	if (oidc_util_spaced_string_contains(r->pool, requested_response_type,
2740 			OIDC_PROTO_RESPONSE_TYPE_CODE)) {
2741 		if (code == NULL) {
2742 			oidc_error(r,
2743 					"the requested response type was (%s) but the response does not contain a \"%s\" parameter",
2744 					requested_response_type, OIDC_PROTO_CODE);
2745 			return FALSE;
2746 		}
2747 	} else if (code != NULL) {
2748 		oidc_error(r,
2749 				"the requested response type was (%s) but the response contains a \"%s\" parameter",
2750 				requested_response_type, OIDC_PROTO_CODE);
2751 		return FALSE;
2752 	}
2753 
2754 	if (oidc_util_spaced_string_contains(r->pool, requested_response_type,
2755 			OIDC_PROTO_RESPONSE_TYPE_IDTOKEN)) {
2756 		if (id_token == NULL) {
2757 			oidc_error(r,
2758 					"the requested response type was (%s) but the response does not contain an \"%s\" parameter",
2759 					requested_response_type, OIDC_PROTO_ID_TOKEN);
2760 			return FALSE;
2761 		}
2762 	} else if (id_token != NULL) {
2763 		oidc_error(r,
2764 				"the requested response type was (%s) but the response contains an \"%s\" parameter",
2765 				requested_response_type, OIDC_PROTO_ID_TOKEN);
2766 		return FALSE;
2767 	}
2768 
2769 	if (oidc_util_spaced_string_contains(r->pool, requested_response_type,
2770 			OIDC_PROTO_RESPONSE_TYPE_TOKEN)) {
2771 		if (access_token == NULL) {
2772 			oidc_error(r,
2773 					"the requested response type was (%s) but the response does not contain an \"%s\" parameter",
2774 					requested_response_type, OIDC_PROTO_ACCESS_TOKEN);
2775 			return FALSE;
2776 		}
2777 	} else if (access_token != NULL) {
2778 		oidc_error(r,
2779 				"the requested response type was (%s) but the response contains an \"%s\" parameter",
2780 				requested_response_type, OIDC_PROTO_ACCESS_TOKEN);
2781 		return FALSE;
2782 	}
2783 
2784 	return TRUE;
2785 }
2786 
2787 /*
2788  * validate the response mode used by the OP against the requested response mode
2789  */
oidc_proto_validate_response_mode(request_rec * r,oidc_proto_state_t * proto_state,const char * response_mode,const char * default_response_mode)2790 static apr_byte_t oidc_proto_validate_response_mode(request_rec *r,
2791 		oidc_proto_state_t *proto_state, const char *response_mode,
2792 		const char *default_response_mode) {
2793 
2794 	const char *requested_response_mode = oidc_proto_state_get_response_mode(
2795 			proto_state);
2796 	if (requested_response_mode == NULL)
2797 		requested_response_mode = default_response_mode;
2798 
2799 	if (apr_strnatcmp(requested_response_mode, response_mode) != 0) {
2800 		oidc_error(r,
2801 				"requested response mode (%s) does not match the response mode used by the OP (%s)",
2802 				requested_response_mode, response_mode);
2803 		return FALSE;
2804 	}
2805 
2806 	return TRUE;
2807 }
2808 
2809 /*
2810  * validate the client_id/iss provided by the OP against the client_id/iss registered with the provider that the request was sent to
2811  */
oidc_proto_validate_issuer_client_id(request_rec * r,const char * configured_issuer,const char * response_issuer,const char * configured_client_id,const char * response_client_id)2812 static apr_byte_t oidc_proto_validate_issuer_client_id(request_rec *r,
2813 		const char *configured_issuer, const char *response_issuer,
2814 		const char *configured_client_id, const char *response_client_id) {
2815 
2816 	if (response_issuer != NULL) {
2817 		if (apr_strnatcmp(configured_issuer, response_issuer) != 0) {
2818 			oidc_error(r,
2819 					"configured issuer (%s) does not match the issuer provided in the response by the OP (%s)",
2820 					configured_issuer, response_issuer);
2821 			return FALSE;
2822 		}
2823 	}
2824 
2825 	if (response_client_id != NULL) {
2826 		if (apr_strnatcmp(configured_client_id, response_client_id) != 0) {
2827 			oidc_error(r,
2828 					"configured client_id (%s) does not match the client_id provided in the response by the OP (%s)",
2829 					configured_client_id, response_client_id);
2830 			return FALSE;
2831 		}
2832 	}
2833 
2834 	oidc_debug(r, "iss and/or client_id matched OK: %s, %s, %s, %s",
2835 			response_issuer, configured_issuer, response_client_id,
2836 			configured_client_id);
2837 
2838 	return TRUE;
2839 }
2840 
2841 /*
2842  * helper function to validate both the response type and the response mode in a single function call
2843  */
oidc_proto_validate_response_type_mode_issuer(request_rec * r,const char * requested_response_type,apr_table_t * params,oidc_proto_state_t * proto_state,const char * response_mode,const char * default_response_mode,const char * issuer,const char * c_client_id)2844 static apr_byte_t oidc_proto_validate_response_type_mode_issuer(request_rec *r,
2845 		const char *requested_response_type, apr_table_t *params,
2846 		oidc_proto_state_t *proto_state, const char *response_mode,
2847 		const char *default_response_mode, const char *issuer,
2848 		const char *c_client_id) {
2849 
2850 	const char *code = apr_table_get(params, OIDC_PROTO_CODE);
2851 	const char *id_token = apr_table_get(params, OIDC_PROTO_ID_TOKEN);
2852 	const char *access_token = apr_table_get(params,
2853 			OIDC_PROTO_ACCESS_TOKEN);
2854 	const char *iss = apr_table_get(params, OIDC_PROTO_ISS);
2855 	const char *client_id = apr_table_get(params, OIDC_PROTO_CLIENT_ID);
2856 
2857 	if (oidc_proto_validate_issuer_client_id(r, issuer, iss, c_client_id,
2858 			client_id) == FALSE)
2859 		return FALSE;
2860 
2861 	if (oidc_proto_validate_response_type(r, requested_response_type, code,
2862 			id_token, access_token) == FALSE)
2863 		return FALSE;
2864 
2865 	if (oidc_proto_validate_response_mode(r, proto_state, response_mode,
2866 			default_response_mode) == FALSE)
2867 		return FALSE;
2868 
2869 	return TRUE;
2870 }
2871 
2872 /*
2873  * parse and id_token and check the c_hash if the code is provided
2874  */
oidc_proto_parse_idtoken_and_validate_code(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,const char * response_type,apr_table_t * params,oidc_jwt_t ** jwt,apr_byte_t must_validate_code)2875 static apr_byte_t oidc_proto_parse_idtoken_and_validate_code(request_rec *r,
2876 		oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
2877 		const char *response_type, apr_table_t *params, oidc_jwt_t **jwt,
2878 		apr_byte_t must_validate_code) {
2879 
2880 	const char *code = apr_table_get(params, OIDC_PROTO_CODE);
2881 	const char *id_token = apr_table_get(params, OIDC_PROTO_ID_TOKEN);
2882 
2883 	apr_byte_t is_code_flow = (oidc_util_spaced_string_contains(r->pool,
2884 			response_type, OIDC_PROTO_RESPONSE_TYPE_CODE) == TRUE)
2885 					&& (oidc_util_spaced_string_contains(r->pool, response_type,
2886 							OIDC_PROTO_RESPONSE_TYPE_IDTOKEN) == FALSE);
2887 
2888 	const char *nonce = oidc_proto_state_get_nonce(proto_state);
2889 	if (oidc_proto_parse_idtoken(r, c, provider, id_token, nonce, jwt,
2890 			is_code_flow) == FALSE)
2891 		return FALSE;
2892 
2893 	if ((must_validate_code == TRUE)
2894 			&& (oidc_proto_validate_code(r, provider, *jwt, response_type, code)
2895 					== FALSE))
2896 		return FALSE;
2897 
2898 	return TRUE;
2899 }
2900 
2901 /*
2902  * resolve the code against the token endpoint and validate the response that is returned by the OP
2903  */
oidc_proto_resolve_code_and_validate_response(request_rec * r,oidc_cfg * c,oidc_provider_t * provider,const char * response_type,apr_table_t * params,oidc_proto_state_t * proto_state)2904 static apr_byte_t oidc_proto_resolve_code_and_validate_response(request_rec *r,
2905 		oidc_cfg *c, oidc_provider_t *provider, const char *response_type,
2906 		apr_table_t *params, oidc_proto_state_t *proto_state) {
2907 
2908 	char *id_token = NULL;
2909 	char *access_token = NULL;
2910 	char *token_type = NULL;
2911 	int expires_in = -1;
2912 	char *refresh_token = NULL;
2913 	char *code_verifier = NULL;
2914 
2915 	if (provider->pkce != NULL)
2916 		provider->pkce->verifier(r,
2917 				oidc_proto_state_get_pkce_state(proto_state), &code_verifier);
2918 
2919 	const char *state = oidc_proto_state_get_state(proto_state);
2920 
2921 	if (oidc_proto_resolve_code(r, c, provider,
2922 			apr_table_get(params, OIDC_PROTO_CODE), code_verifier, &id_token,
2923 			&access_token, &token_type, &expires_in, &refresh_token,
2924 			state) == FALSE) {
2925 		oidc_error(r, "failed to resolve the code");
2926 		return FALSE;
2927 	}
2928 
2929 	if (oidc_proto_validate_code_response(r, response_type, id_token,
2930 			access_token, token_type) == FALSE) {
2931 		oidc_error(r, "code response validation failed");
2932 		return FALSE;
2933 	}
2934 
2935 	/* don't override parameters that may already have been (rightfully) set in the authorization response */
2936 	if ((apr_table_get(params, OIDC_PROTO_ID_TOKEN) == NULL)
2937 			&& (id_token != NULL)) {
2938 		apr_table_set(params, OIDC_PROTO_ID_TOKEN, id_token);
2939 	}
2940 
2941 	if ((apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN) == NULL)
2942 			&& (access_token != NULL)) {
2943 		apr_table_set(params, OIDC_PROTO_ACCESS_TOKEN, access_token);
2944 		if (token_type != NULL)
2945 			apr_table_set(params, OIDC_PROTO_TOKEN_TYPE, token_type);
2946 		if (expires_in != -1)
2947 			apr_table_setn(params, OIDC_PROTO_EXPIRES_IN,
2948 					apr_psprintf(r->pool, "%d", expires_in));
2949 	}
2950 
2951 	/* refresh token should not have been set before */
2952 	if (refresh_token != NULL) {
2953 		apr_table_set(params, OIDC_PROTO_REFRESH_TOKEN, refresh_token);
2954 	}
2955 
2956 	return TRUE;
2957 }
2958 
2959 /*
2960  * handle the "code id_token" response type
2961  */
oidc_proto_authorization_response_code_idtoken(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)2962 apr_byte_t oidc_proto_authorization_response_code_idtoken(request_rec *r,
2963 		oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
2964 		apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
2965 
2966 	oidc_debug(r, "enter");
2967 
2968 	static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN;
2969 
2970 	if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
2971 			proto_state, response_mode,
2972 			OIDC_PROTO_RESPONSE_MODE_FRAGMENT, provider->issuer,
2973 			provider->client_id) == FALSE)
2974 		return FALSE;
2975 
2976 	if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
2977 			response_type, params, jwt, TRUE) == FALSE)
2978 		return FALSE;
2979 
2980 	/* clear parameters that should only be set from the token endpoint */
2981 	apr_table_unset(params, OIDC_PROTO_ACCESS_TOKEN);
2982 	apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE);
2983 	apr_table_unset(params, OIDC_PROTO_EXPIRES_IN);
2984 	apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
2985 
2986 	if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
2987 			response_type, params, proto_state) == FALSE)
2988 		return FALSE;
2989 
2990 	return TRUE;
2991 }
2992 
2993 /*
2994  * handle the "code token" response type
2995  */
oidc_proto_handle_authorization_response_code_token(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)2996 apr_byte_t oidc_proto_handle_authorization_response_code_token(request_rec *r,
2997 		oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
2998 		apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
2999 
3000 	oidc_debug(r, "enter");
3001 
3002 	static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_CODE_TOKEN;
3003 
3004 	if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
3005 			proto_state, response_mode,
3006 			OIDC_PROTO_RESPONSE_MODE_FRAGMENT, provider->issuer,
3007 			provider->client_id) == FALSE)
3008 		return FALSE;
3009 
3010 	/* clear parameters that should only be set from the token endpoint */
3011 	apr_table_unset(params, OIDC_PROTO_ID_TOKEN);
3012 	apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3013 
3014 	if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
3015 			response_type, params, proto_state) == FALSE)
3016 		return FALSE;
3017 
3018 	if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
3019 			response_type, params, jwt, FALSE) == FALSE)
3020 		return FALSE;
3021 
3022 	return TRUE;
3023 }
3024 
3025 /*
3026  * handle the "code" response type
3027  */
oidc_proto_handle_authorization_response_code(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3028 apr_byte_t oidc_proto_handle_authorization_response_code(request_rec *r,
3029 		oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
3030 		apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
3031 
3032 	oidc_debug(r, "enter");
3033 
3034 	static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_CODE;
3035 
3036 	if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
3037 			proto_state, response_mode,
3038 			OIDC_PROTO_RESPONSE_MODE_QUERY, provider->issuer,
3039 			provider->client_id) == FALSE)
3040 		return FALSE;
3041 
3042 	/* clear parameters that should only be set from the token endpoint */
3043 	apr_table_unset(params, OIDC_PROTO_ACCESS_TOKEN);
3044 	apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE);
3045 	apr_table_unset(params, OIDC_PROTO_EXPIRES_IN);
3046 	apr_table_unset(params, OIDC_PROTO_ID_TOKEN);
3047 	apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3048 
3049 	if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
3050 			response_type, params, proto_state) == FALSE)
3051 		return FALSE;
3052 
3053 	/*
3054 	 * in this flow it is actually optional to check the code token against the c_hash
3055 	 */
3056 	if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
3057 			response_type, params, jwt, TRUE) == FALSE)
3058 		return FALSE;
3059 
3060 	/*
3061 	 * in this flow it is actually optional to check the access token against the at_hash
3062 	 */
3063 	if ((apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN) != NULL)
3064 			&& (oidc_proto_validate_access_token(r, provider, *jwt,
3065 					response_type,
3066 					apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN)) == FALSE))
3067 		return FALSE;
3068 
3069 	return TRUE;
3070 }
3071 
3072 /*
3073  * helper function for implicit flows: shared code for "id_token token" and "id_token"
3074  */
oidc_proto_handle_implicit_flow(request_rec * r,oidc_cfg * c,const char * response_type,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3075 static apr_byte_t oidc_proto_handle_implicit_flow(request_rec *r, oidc_cfg *c,
3076 		const char *response_type, oidc_proto_state_t *proto_state,
3077 		oidc_provider_t *provider, apr_table_t *params,
3078 		const char *response_mode, oidc_jwt_t **jwt) {
3079 
3080 	if (oidc_proto_validate_response_type_mode_issuer(r, response_type, params,
3081 			proto_state, response_mode,
3082 			OIDC_PROTO_RESPONSE_MODE_FRAGMENT, provider->issuer,
3083 			provider->client_id) == FALSE)
3084 		return FALSE;
3085 
3086 	if (oidc_proto_parse_idtoken_and_validate_code(r, c, proto_state, provider,
3087 			response_type, params, jwt, TRUE) == FALSE)
3088 		return FALSE;
3089 
3090 	return TRUE;
3091 }
3092 
3093 /*
3094  * handle the "code id_token token" response type
3095  */
oidc_proto_authorization_response_code_idtoken_token(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3096 apr_byte_t oidc_proto_authorization_response_code_idtoken_token(request_rec *r,
3097 		oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
3098 		apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
3099 
3100 	oidc_debug(r, "enter");
3101 
3102 	static const char *response_type =
3103 			OIDC_PROTO_RESPONSE_TYPE_CODE_IDTOKEN_TOKEN;
3104 
3105 	if (oidc_proto_handle_implicit_flow(r, c, response_type, proto_state,
3106 			provider, params, response_mode, jwt) == FALSE)
3107 		return FALSE;
3108 
3109 	if (oidc_proto_validate_access_token(r, provider, *jwt, response_type,
3110 			apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN)) == FALSE)
3111 		return FALSE;
3112 
3113 	/* clear parameters that should only be set from the token endpoint */
3114 	apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3115 
3116 	if (oidc_proto_resolve_code_and_validate_response(r, c, provider,
3117 			response_type, params, proto_state) == FALSE)
3118 		return FALSE;
3119 
3120 	return TRUE;
3121 }
3122 
3123 /*
3124  * handle the "id_token token" response type
3125  */
oidc_proto_handle_authorization_response_idtoken_token(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3126 apr_byte_t oidc_proto_handle_authorization_response_idtoken_token(
3127 		request_rec *r, oidc_cfg *c, oidc_proto_state_t *proto_state,
3128 		oidc_provider_t *provider, apr_table_t *params,
3129 		const char *response_mode, oidc_jwt_t **jwt) {
3130 
3131 	oidc_debug(r, "enter");
3132 
3133 	static const char *response_type =
3134 			OIDC_PROTO_RESPONSE_TYPE_IDTOKEN_TOKEN;
3135 
3136 	if (oidc_proto_handle_implicit_flow(r, c, response_type, proto_state,
3137 			provider, params, response_mode, jwt) == FALSE)
3138 		return FALSE;
3139 
3140 	if (oidc_proto_validate_access_token(r, provider, *jwt, response_type,
3141 			apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN)) == FALSE)
3142 		return FALSE;
3143 
3144 	/* clear parameters that should not be part of this flow */
3145 	apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3146 
3147 	return TRUE;
3148 }
3149 
3150 /*
3151  * handle the "id_token" response type
3152  */
oidc_proto_handle_authorization_response_idtoken(request_rec * r,oidc_cfg * c,oidc_proto_state_t * proto_state,oidc_provider_t * provider,apr_table_t * params,const char * response_mode,oidc_jwt_t ** jwt)3153 apr_byte_t oidc_proto_handle_authorization_response_idtoken(request_rec *r,
3154 		oidc_cfg *c, oidc_proto_state_t *proto_state, oidc_provider_t *provider,
3155 		apr_table_t *params, const char *response_mode, oidc_jwt_t **jwt) {
3156 
3157 	oidc_debug(r, "enter");
3158 
3159 	static const char *response_type = OIDC_PROTO_RESPONSE_TYPE_IDTOKEN;
3160 
3161 	if (oidc_proto_handle_implicit_flow(r, c, response_type, proto_state,
3162 			provider, params, response_mode, jwt) == FALSE)
3163 		return FALSE;
3164 
3165 	/* clear parameters that should not be part of this flow */
3166 	apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE);
3167 	apr_table_unset(params, OIDC_PROTO_EXPIRES_IN);
3168 	apr_table_unset(params, OIDC_PROTO_REFRESH_TOKEN);
3169 
3170 	return TRUE;
3171 }
3172