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  * OpenID Connect metadata handling routines, for both OP discovery and client registration
42  *
43  * @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
44  */
45 
46 #include <apr_hash.h>
47 #include <apr_time.h>
48 #include <apr_strings.h>
49 #include <apr_pools.h>
50 
51 #include <httpd.h>
52 #include <http_log.h>
53 
54 #include "mod_auth_openidc.h"
55 #include "parse.h"
56 
57 extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
58 
59 #define OIDC_METADATA_SUFFIX_PROVIDER                              "provider"
60 #define OIDC_METADATA_SUFFIX_CLIENT                                "client"
61 #define OIDC_METADATA_SUFFIX_CONF                                  "conf"
62 
63 #define OIDC_METADATA_ISSUER                                       "issuer"
64 #define OIDC_METADATA_RESPONSE_TYPES_SUPPORTED                     "response_types_supported"
65 #define OIDC_METADATA_RESPONSE_MODES_SUPPORTED                     "response_modes_supported"
66 #define OIDC_METADATA_AUTHORIZATION_ENDPOINT                       "authorization_endpoint"
67 #define OIDC_METADATA_TOKEN_ENDPOINT                               "token_endpoint"
68 #define OIDC_METADATA_INTROSPECTION_ENDPOINT                       "introspection_endpoint"
69 #define OIDC_METADATA_USERINFO_ENDPOINT                            "userinfo_endpoint"
70 #define OIDC_METADATA_REVOCATION_ENDPOINT                          "revocation_endpoint"
71 #define OIDC_METADATA_JWKS_URI                                     "jwks_uri"
72 #define OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED        "token_endpoint_auth_methods_supported"
73 #define OIDC_METADATA_INTROSPECTON_ENDPOINT_AUTH_METHODS_SUPPORTED "introspection_endpoint_auth_methods_supported"
74 #define OIDC_METADATA_REGISTRATION_ENDPOINT                        "registration_endpoint"
75 #define OIDC_METADATA_CHECK_SESSION_IFRAME                         "check_session_iframe"
76 #define OIDC_METADATA_BACKCHANNEL_LOGOUT_SUPPORTED                 "backchannel_logout_supported"
77 
78 #define OIDC_METADATA_END_SESSION_ENDPOINT                         "end_session_endpoint"
79 #define OIDC_METADATA_CLIENT_ID                                    "client_id"
80 #define OIDC_METADATA_CLIENT_SECRET                                "client_secret"
81 #define OIDC_METADATA_CLIENT_SECRET_EXPIRES_AT                     "client_secret_expires_at"
82 
83 #define OIDC_METADATA_KEYS                                         "keys"
84 
85 #define OIDC_METADATA_CLIENT_JWKS_URI                              "client_jwks_uri"
86 #define OIDC_METADATA_ID_TOKEN_SIGNED_RESPONSE_ALG                 "id_token_signed_response_alg"
87 #define OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ALG              "id_token_encrypted_response_alg"
88 #define OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ENC              "id_token_encrypted_response_enc"
89 #define OIDC_METADATA_USERINFO_SIGNED_RESPONSE_ALG                 "userinfo_signed_response_alg"
90 #define OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ALG              "userinfo_encrypted_response_alg"
91 #define OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ENC              "userinfo_encrypted_response_enc"
92 
93 #define OIDC_METADATA_CLIENT_NAME                                  "client_name"
94 #define OIDC_METADATA_REDIRECT_URIS                                "redirect_uris"
95 #define OIDC_METADATA_RESPONSE_TYPES                               "response_types"
96 #define OIDC_METADATA_GRANT_TYPES                                  "grant_types"
97 #define OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHOD                   "token_endpoint_auth_method"
98 #define OIDC_METADATA_CONTACTS                                     "contacts"
99 #define OIDC_METADATA_INITIATE_LOGIN_URI                           "initiate_login_uri"
100 #define OIDC_METADATA_FRONTCHANNEL_LOGOUT_URI                      "frontchannel_logout_uri"
101 #define OIDC_METADATA_BACKCHANNEL_LOGOUT_URI                       "backchannel_logout_uri"
102 #define OIDC_METADATA_POST_LOGOUT_REDIRECT_URIS                    "post_logout_redirect_uris"
103 #define OIDC_METADATA_IDTOKEN_BINDING_CNF                          "id_token_token_binding_cnf"
104 #define OIDC_METADATA_SSL_VALIDATE_SERVER                          "ssl_validate_server"
105 #define OIDC_METADATA_VALIDATE_ISSUER                              "validate_issuer"
106 #define OIDC_METADATA_SCOPE                                        "scope"
107 #define OIDC_METADATA_JWKS_REFRESH_INTERVAL                        "jwks_refresh_interval"
108 #define OIDC_METADATA_IDTOKEN_IAT_SLACK                            "idtoken_iat_slack"
109 #define OIDC_METADATA_SESSION_MAX_DURATION                         "session_max_duration"
110 #define OIDC_METADATA_AUTH_REQUEST_PARAMS                          "auth_request_params"
111 #define OIDC_METADATA_TOKEN_ENDPOINT_PARAMS                        "token_endpoint_params"
112 #define OIDC_METADATA_RESPONSE_MODE                                "response_mode"
113 #define OIDC_METADATA_PKCE_METHOD                                  "pkce_method"
114 #define OIDC_METADATA_CLIENT_CONTACT                               "client_contact"
115 #define OIDC_METADATA_TOKEN_ENDPOINT_AUTH                          "token_endpoint_auth"
116 #define OIDC_METADATA_REGISTRATION_TOKEN                           "registration_token"
117 #define OIDC_METADATA_REGISTRATION_ENDPOINT_JSON                   "registration_endpoint_json"
118 #define OIDC_METADATA_RESPONSE_TYPE                                "response_type"
119 #define OIDC_METADATA_USERINFO_REFRESH_INTERVAL                    "userinfo_refresh_interval"
120 #define OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_CERT               "token_endpoint_tls_client_cert"
121 #define OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_KEY                "token_endpoint_tls_client_key"
122 #define OIDC_METADATA_REQUEST_OBJECT                               "request_object"
123 #define OIDC_METADATA_USERINFO_TOKEN_METHOD                        "userinfo_token_method"
124 #define OIDC_METADATA_TOKEN_BINDING_POLICY                         "token_binding_policy"
125 #define OIDC_METADATA_AUTH_REQUEST_METHOD                          "auth_request_method"
126 #define OIDC_METADATA_ISSUER_SPECIFIC_REDIRECT_URI                 "issuer_specific_redirect_uri"
127 
128 /*
129  * get the metadata filename for a specified issuer (cq. urlencode it)
130  */
oidc_metadata_issuer_to_filename(request_rec * r,const char * issuer)131 static const char* oidc_metadata_issuer_to_filename(request_rec *r,
132 		const char *issuer) {
133 
134 	/* strip leading https:// */
135 	char *p = strstr(issuer, "https://");
136 	if (p == issuer) {
137 		p = apr_pstrdup(r->pool, issuer + strlen("https://"));
138 	} else {
139 		p = strstr(issuer, "http://");
140 		if (p == issuer) {
141 			p = apr_pstrdup(r->pool, issuer + strlen("http://"));
142 		} else {
143 			p = apr_pstrdup(r->pool, issuer);
144 		}
145 	}
146 
147 	/* strip trailing '/' */
148 	int n = strlen(p);
149 	if (p[n - 1] == OIDC_CHAR_FORWARD_SLASH)
150 		p[n - 1] = '\0';
151 
152 	return oidc_util_escape_string(r, p);
153 }
154 
155 /*
156  * get the issuer from a metadata filename (cq. urldecode it)
157  */
oidc_metadata_filename_to_issuer(request_rec * r,const char * filename)158 static const char* oidc_metadata_filename_to_issuer(request_rec *r,
159 		const char *filename) {
160 	char *result = apr_pstrdup(r->pool, filename);
161 	char *p = strrchr(result, OIDC_CHAR_DOT);
162 	*p = '\0';
163 	p = oidc_util_unescape_string(r, result);
164 	return apr_psprintf(r->pool, "https://%s", p);
165 }
166 
167 /*
168  * get the full path to the metadata file for a specified issuer and directory
169  */
oidc_metadata_file_path(request_rec * r,oidc_cfg * cfg,const char * issuer,const char * type)170 static const char* oidc_metadata_file_path(request_rec *r, oidc_cfg *cfg,
171 		const char *issuer, const char *type) {
172 	return apr_psprintf(r->pool, "%s/%s.%s", cfg->metadata_dir,
173 			oidc_metadata_issuer_to_filename(r, issuer), type);
174 }
175 
176 /*
177  * get the full path to the provider metadata file for a specified issuer
178  */
oidc_metadata_provider_file_path(request_rec * r,const char * issuer)179 static const char* oidc_metadata_provider_file_path(request_rec *r,
180 		const char *issuer) {
181 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
182 			&auth_openidc_module);
183 	return oidc_metadata_file_path(r, cfg, issuer,
184 			OIDC_METADATA_SUFFIX_PROVIDER);
185 }
186 
187 /*
188  * get the full path to the client metadata file for a specified issuer
189  */
oidc_metadata_client_file_path(request_rec * r,const char * issuer)190 static const char* oidc_metadata_client_file_path(request_rec *r,
191 		const char *issuer) {
192 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
193 			&auth_openidc_module);
194 	return oidc_metadata_file_path(r, cfg, issuer, OIDC_METADATA_SUFFIX_CLIENT);
195 }
196 
197 /*
198  * get the full path to the custom config file for a specified issuer
199  */
oidc_metadata_conf_path(request_rec * r,const char * issuer)200 static const char* oidc_metadata_conf_path(request_rec *r, const char *issuer) {
201 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
202 			&auth_openidc_module);
203 	return oidc_metadata_file_path(r, cfg, issuer, OIDC_METADATA_SUFFIX_CONF);
204 }
205 
206 /*
207  * get cache key for the JWKs file for a specified URI
208  */
oidc_metadata_jwks_cache_key(request_rec * r,const char * jwks_uri)209 static const char* oidc_metadata_jwks_cache_key(request_rec *r,
210 		const char *jwks_uri) {
211 	return jwks_uri;
212 }
213 
214 /*
215  * read a JSON metadata file from disk
216  */
oidc_metadata_file_read_json(request_rec * r,const char * path,json_t ** result)217 static apr_byte_t oidc_metadata_file_read_json(request_rec *r, const char *path,
218 		json_t **result) {
219 	char *buf = NULL;
220 
221 	/* read the file contents */
222 	if (oidc_util_file_read(r, path, r->pool, &buf) == FALSE)
223 		return FALSE;
224 
225 	/* decode the JSON contents of the buffer */
226 	return oidc_util_decode_json_object(r, buf, result);
227 }
228 
229 /*
230  * check if the specified entry in metadata is a valid URI
231  */
oidc_metadata_is_valid_uri(request_rec * r,const char * type,const char * issuer,json_t * json,const char * key,char ** value,apr_byte_t is_mandatory)232 static apr_byte_t oidc_metadata_is_valid_uri(request_rec *r, const char *type,
233 		const char *issuer, json_t *json, const char *key, char **value,
234 		apr_byte_t is_mandatory) {
235 
236 	char *s_value = NULL;
237 	oidc_json_object_get_string(r->pool, json, key, &s_value, NULL);
238 
239 	if (s_value == NULL) {
240 		if (is_mandatory) {
241 			oidc_error(r,
242 					"%s (%s) JSON metadata does not contain the mandatory \"%s\" string entry",
243 					type, issuer, key);
244 		}
245 		return (!is_mandatory);
246 	}
247 
248 	if (oidc_valid_http_url(r->pool, s_value) != NULL) {
249 		oidc_warn(r, "\"%s\" is not a valid http URL for key \"%s\"", s_value,
250 				key);
251 		return FALSE;
252 	}
253 
254 	if (value)
255 		*value = s_value;
256 
257 	return TRUE;
258 }
259 
260 /*
261  * check to see if JSON provider metadata is valid
262  */
oidc_metadata_provider_is_valid(request_rec * r,oidc_cfg * cfg,json_t * j_provider,const char * issuer)263 apr_byte_t oidc_metadata_provider_is_valid(request_rec *r, oidc_cfg *cfg,
264 		json_t *j_provider, const char *issuer) {
265 
266 	/* get the "issuer" from the provider metadata and double-check that it matches what we looked for */
267 	char *s_issuer = NULL;
268 	oidc_json_object_get_string(r->pool, j_provider, OIDC_METADATA_ISSUER,
269 			&s_issuer, NULL);
270 	if (s_issuer == NULL) {
271 		oidc_error(r,
272 				"provider (%s) JSON metadata did not contain an \"" OIDC_METADATA_ISSUER "\" string",
273 				issuer);
274 		return FALSE;
275 	}
276 
277 	/* check that the issuer matches */
278 	if (issuer != NULL) {
279 		if (oidc_util_issuer_match(issuer, s_issuer) == FALSE) {
280 			oidc_error(r,
281 					"requested issuer (%s) does not match the \"" OIDC_METADATA_ISSUER "\" value in the provider metadata file: %s",
282 					issuer, s_issuer);
283 			return FALSE;
284 		}
285 	}
286 
287 	/* verify that the provider supports the a flow that we implement */
288 	if (oidc_valid_string_in_array(r->pool, j_provider,
289 			OIDC_METADATA_RESPONSE_TYPES_SUPPORTED, oidc_valid_response_type, NULL,
290 			FALSE, NULL) != NULL) {
291 		if (json_object_get(j_provider,
292 				OIDC_METADATA_RESPONSE_TYPES_SUPPORTED) != NULL) {
293 			oidc_error(r,
294 					"could not find a supported response type in provider metadata (%s) for entry \"" OIDC_METADATA_RESPONSE_TYPES_SUPPORTED "\"",
295 					issuer);
296 			return FALSE;
297 		}
298 		oidc_warn(r,
299 				"could not find (required) supported response types  (\"" OIDC_METADATA_RESPONSE_TYPES_SUPPORTED "\") in provider metadata (%s); assuming that \"code\" flow is supported...",
300 				issuer);
301 	}
302 
303 	/* verify that the provider supports a response_mode that we implement */
304 	if (oidc_valid_string_in_array(r->pool, j_provider,
305 			OIDC_METADATA_RESPONSE_MODES_SUPPORTED, oidc_valid_response_mode, NULL,
306 			TRUE, NULL) != NULL) {
307 		oidc_error(r,
308 				"could not find a supported response mode in provider metadata (%s) for entry \"" OIDC_METADATA_RESPONSE_MODES_SUPPORTED "\"",
309 				issuer);
310 		return FALSE;
311 	}
312 
313 	/* check the required authorization endpoint */
314 	if (oidc_metadata_is_valid_uri(r, OIDC_METADATA_SUFFIX_PROVIDER, issuer,
315 			j_provider,
316 			OIDC_METADATA_AUTHORIZATION_ENDPOINT, NULL, TRUE) == FALSE)
317 		return FALSE;
318 
319 	/* check the optional token endpoint */
320 	if (oidc_metadata_is_valid_uri(r, OIDC_METADATA_SUFFIX_PROVIDER, issuer,
321 			j_provider,
322 			OIDC_METADATA_TOKEN_ENDPOINT, NULL, FALSE) == FALSE)
323 		return FALSE;
324 
325 	/* check the optional user info endpoint */
326 	if (oidc_metadata_is_valid_uri(r, OIDC_METADATA_SUFFIX_PROVIDER, issuer,
327 			j_provider,
328 			OIDC_METADATA_USERINFO_ENDPOINT, NULL, FALSE) == FALSE)
329 		return FALSE;
330 
331 	/* check the optional JWKs URI */
332 	if (oidc_metadata_is_valid_uri(r, OIDC_METADATA_SUFFIX_PROVIDER, issuer,
333 			j_provider,
334 			OIDC_METADATA_JWKS_URI, NULL, FALSE) == FALSE)
335 		return FALSE;
336 
337 	/* find out what type of authentication the token endpoint supports */
338 	if (oidc_valid_string_in_array(r->pool, j_provider,
339 			OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,
340 			oidc_cfg_get_valid_endpoint_auth_function(cfg), NULL,
341 			TRUE, NULL) != NULL) {
342 		oidc_error(r,
343 				"could not find a supported token endpoint authentication method in provider metadata (%s) for entry \"" OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED "\"",
344 				issuer);
345 		return FALSE;
346 	}
347 
348 	return TRUE;
349 }
350 
351 /*
352  * check to see if dynamically registered JSON client metadata is valid and has not expired
353  */
oidc_metadata_client_is_valid(request_rec * r,json_t * j_client,const char * issuer)354 static apr_byte_t oidc_metadata_client_is_valid(request_rec *r,
355 		json_t *j_client, const char *issuer) {
356 
357 	char *str;
358 
359 	/* get a handle to the client_id we need to use for this provider */
360 	str = NULL;
361 	oidc_json_object_get_string(r->pool, j_client, OIDC_METADATA_CLIENT_ID,
362 			&str, NULL);
363 	if (str == NULL) {
364 		oidc_error(r,
365 				"client (%s) JSON metadata did not contain a \"" OIDC_METADATA_CLIENT_ID "\" string",
366 				issuer);
367 		return FALSE;
368 	}
369 
370 	/* get a handle to the client_secret we need to use for this provider */
371 	str = NULL;
372 	oidc_json_object_get_string(r->pool, j_client, OIDC_METADATA_CLIENT_SECRET,
373 			&str, NULL);
374 	if (str == NULL) {
375 		oidc_warn(r,
376 				"client (%s) JSON metadata did not contain a \"" OIDC_METADATA_CLIENT_SECRET "\" string",
377 				issuer);
378 	}
379 
380 	/* the expiry timestamp from the JSON object */
381 	json_t *expires_at = json_object_get(j_client,
382 			OIDC_METADATA_CLIENT_SECRET_EXPIRES_AT);
383 	if ((expires_at == NULL) || (!json_is_integer(expires_at))) {
384 		oidc_debug(r,
385 				"client (%s) metadata did not contain a \"" OIDC_METADATA_CLIENT_SECRET_EXPIRES_AT "\" setting",
386 				issuer);
387 		/* assume that it never expires */
388 		return TRUE;
389 	}
390 
391 	/* see if it is unrestricted */
392 	if (json_integer_value(expires_at) == 0) {
393 		oidc_debug(r,
394 				"client (%s) metadata never expires (" OIDC_METADATA_CLIENT_SECRET_EXPIRES_AT "=0)",
395 				issuer);
396 		return TRUE;
397 	}
398 
399 	/* check if the value >= now */
400 	if (apr_time_sec(apr_time_now()) > json_integer_value(expires_at)) {
401 		oidc_warn(r, "client (%s) secret expired", issuer);
402 		return FALSE;
403 	}
404 
405 	oidc_debug(r, "client (%s) metadata is valid", issuer);
406 
407 	return TRUE;
408 }
409 
410 /*
411  * checks if a parsed JWKs file is a valid one, cq. contains "keys"
412  */
oidc_metadata_jwks_is_valid(request_rec * r,const oidc_jwks_uri_t * jwks_uri,json_t * j_jwks)413 static apr_byte_t oidc_metadata_jwks_is_valid(request_rec *r,
414 		const oidc_jwks_uri_t *jwks_uri, json_t *j_jwks) {
415 
416 	json_t *keys = json_object_get(j_jwks, OIDC_METADATA_KEYS);
417 	if ((keys == NULL) || (!json_is_array(keys))) {
418 		oidc_error(r,
419 				"JWKs JSON metadata obtained from URL \"%s\" did not contain a \"" OIDC_METADATA_KEYS "\" array",
420 				jwks_uri->url);
421 		return FALSE;
422 	}
423 	return TRUE;
424 }
425 
426 /*
427  * check is a specified JOSE feature is supported
428  */
oidc_metadata_conf_jose_is_supported(request_rec * r,json_t * j_conf,const char * issuer,const char * key,oidc_valid_function_t valid_function)429 static apr_byte_t oidc_metadata_conf_jose_is_supported(request_rec *r,
430 		json_t *j_conf, const char *issuer, const char *key,
431 		oidc_valid_function_t valid_function) {
432 	char *s_value = NULL;
433 	oidc_json_object_get_string(r->pool, j_conf, key, &s_value, NULL);
434 	if (s_value == NULL)
435 		return TRUE;
436 	const char *rv = valid_function(r->pool, s_value);
437 	if (rv != NULL) {
438 		oidc_error(r,
439 				"(%s) JSON conf data has \"%s\" entry but it contains an unsupported algorithm or encryption type: \"%s\" (%s)",
440 				issuer, key, s_value, rv);
441 		return FALSE;
442 	}
443 	return TRUE;
444 }
445 
446 /*
447  * check to see if JSON configuration data is valid
448  */
oidc_metadata_conf_is_valid(request_rec * r,json_t * j_conf,const char * issuer)449 static apr_byte_t oidc_metadata_conf_is_valid(request_rec *r, json_t *j_conf,
450 		const char *issuer) {
451 
452 	if (oidc_metadata_conf_jose_is_supported(r, j_conf, issuer,
453 			OIDC_METADATA_ID_TOKEN_SIGNED_RESPONSE_ALG,
454 			oidc_valid_signed_response_alg) == FALSE)
455 		return FALSE;
456 	if (oidc_metadata_conf_jose_is_supported(r, j_conf, issuer,
457 			OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ALG,
458 			oidc_valid_encrypted_response_alg) == FALSE)
459 		return FALSE;
460 	if (oidc_metadata_conf_jose_is_supported(r, j_conf, issuer,
461 			OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ENC,
462 			oidc_valid_encrypted_response_enc) == FALSE)
463 		return FALSE;
464 	if (oidc_metadata_conf_jose_is_supported(r, j_conf, issuer,
465 			OIDC_METADATA_USERINFO_SIGNED_RESPONSE_ALG,
466 			oidc_valid_signed_response_alg) == FALSE)
467 		return FALSE;
468 	if (oidc_metadata_conf_jose_is_supported(r, j_conf, issuer,
469 			OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ALG,
470 			oidc_valid_encrypted_response_alg) == FALSE)
471 		return FALSE;
472 	if (oidc_metadata_conf_jose_is_supported(r, j_conf, issuer,
473 			OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ENC,
474 			oidc_valid_encrypted_response_enc) == FALSE)
475 		return FALSE;
476 
477 	return TRUE;
478 }
479 
480 /*
481  * register the client with the OP using Dynamic Client Registration
482  */
oidc_metadata_client_register(request_rec * r,oidc_cfg * cfg,oidc_provider_t * provider,json_t ** j_client,char ** response)483 static apr_byte_t oidc_metadata_client_register(request_rec *r, oidc_cfg *cfg,
484 		oidc_provider_t *provider, json_t **j_client, char **response) {
485 
486 	/* assemble the JSON registration request */
487 	json_t *data = json_object();
488 	json_object_set_new(data, OIDC_METADATA_CLIENT_NAME,
489 			json_string(provider->client_name));
490 	json_object_set_new(data, OIDC_METADATA_REDIRECT_URIS,
491 			json_pack("[s]", oidc_get_redirect_uri_iss(r, cfg, provider)));
492 
493 	json_t *response_types = json_array();
494 	apr_array_header_t *flows = oidc_proto_supported_flows(r->pool);
495 	int i;
496 	for (i = 0; i < flows->nelts; i++) {
497 		json_array_append_new(response_types,
498 				json_string(((const char**) flows->elts)[i]));
499 	}
500 	json_object_set_new(data, OIDC_METADATA_RESPONSE_TYPES, response_types);
501 
502 	json_object_set_new(data, OIDC_METADATA_GRANT_TYPES,
503 			json_pack("[s, s, s]", "authorization_code", "implicit",
504 					"refresh_token"));
505 
506 	if (provider->token_endpoint_auth != NULL) {
507 		json_object_set_new(data, OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHOD,
508 				json_string(provider->token_endpoint_auth));
509 	}
510 
511 	if (provider->client_contact != NULL) {
512 		json_object_set_new(data, OIDC_METADATA_CONTACTS,
513 				json_pack("[s]", provider->client_contact));
514 	}
515 
516 	if (provider->client_jwks_uri) {
517 		json_object_set_new(data, OIDC_METADATA_JWKS_URI,
518 				json_string(provider->client_jwks_uri));
519 	} else if (cfg->public_keys != NULL) {
520 		json_object_set_new(data, OIDC_METADATA_JWKS_URI,
521 				json_string(
522 						apr_psprintf(r->pool, "%s?%s=rsa",
523 								oidc_get_redirect_uri(r, cfg),
524 								OIDC_REDIRECT_URI_REQUEST_JWKS)));
525 	}
526 
527 	if (provider->id_token_signed_response_alg != NULL) {
528 		json_object_set_new(data, OIDC_METADATA_ID_TOKEN_SIGNED_RESPONSE_ALG,
529 				json_string(provider->id_token_signed_response_alg));
530 	}
531 	if (provider->id_token_encrypted_response_alg != NULL) {
532 		json_object_set_new(data, OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ALG,
533 				json_string(provider->id_token_encrypted_response_alg));
534 	}
535 	if (provider->id_token_encrypted_response_enc != NULL) {
536 		json_object_set_new(data, OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ENC,
537 				json_string(provider->id_token_encrypted_response_enc));
538 	}
539 
540 	if (provider->userinfo_signed_response_alg != NULL) {
541 		json_object_set_new(data, OIDC_METADATA_USERINFO_SIGNED_RESPONSE_ALG,
542 				json_string(provider->userinfo_signed_response_alg));
543 	}
544 	if (provider->userinfo_encrypted_response_alg != NULL) {
545 		json_object_set_new(data, OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ALG,
546 				json_string(provider->userinfo_encrypted_response_alg));
547 	}
548 	if (provider->userinfo_encrypted_response_enc != NULL) {
549 		json_object_set_new(data, OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ENC,
550 				json_string(provider->userinfo_encrypted_response_enc));
551 	}
552 
553 	json_object_set_new(data, OIDC_METADATA_INITIATE_LOGIN_URI,
554 			json_string(oidc_get_redirect_uri(r, cfg)));
555 
556 	json_object_set_new(data, OIDC_METADATA_FRONTCHANNEL_LOGOUT_URI,
557 			json_string(
558 					apr_psprintf(r->pool, "%s?%s=%s",
559 							oidc_get_redirect_uri(r, cfg),
560 							OIDC_REDIRECT_URI_REQUEST_LOGOUT,
561 							OIDC_GET_STYLE_LOGOUT_PARAM_VALUE)));
562 
563 	// TODO: may want to add backchannel_logout_session_required
564 	json_object_set_new(data, OIDC_METADATA_BACKCHANNEL_LOGOUT_URI,
565 			json_string(
566 					apr_psprintf(r->pool, "%s?%s=%s",
567 							oidc_get_redirect_uri(r, cfg),
568 							OIDC_REDIRECT_URI_REQUEST_LOGOUT,
569 							OIDC_BACKCHANNEL_STYLE_LOGOUT_PARAM_VALUE)));
570 
571 	if (provider->token_binding_policy > OIDC_TOKEN_BINDING_POLICY_DISABLED) {
572 		json_object_set_new(data, OIDC_METADATA_IDTOKEN_BINDING_CNF,
573 				json_string(OIDC_CLAIM_CNF_TBH));
574 	}
575 
576 	if (cfg->default_slo_url != NULL) {
577 		json_object_set_new(data, OIDC_METADATA_POST_LOGOUT_REDIRECT_URIS,
578 				json_pack("[s]", cfg->default_slo_url));
579 	}
580 
581 	/* add any custom JSON in to the registration request */
582 	if (provider->registration_endpoint_json != NULL) {
583 		json_t *json = NULL;
584 		if (oidc_util_decode_json_object(r,
585 				provider->registration_endpoint_json, &json) == FALSE)
586 			return FALSE;
587 		oidc_util_json_merge(r, json, data);
588 		json_decref(json);
589 	}
590 
591 	/* dynamically register the client with the specified parameters */
592 	if (oidc_util_http_post_json(r, provider->registration_endpoint_url, data,
593 			NULL, provider->registration_token, provider->ssl_validate_server, response,
594 			cfg->http_timeout_short, cfg->outgoing_proxy,
595 			oidc_dir_cfg_pass_cookies(r),
596 			NULL, NULL) == FALSE) {
597 		json_decref(data);
598 		return FALSE;
599 	}
600 	json_decref(data);
601 
602 	/* decode and see if it is not an error response somehow */
603 	if (oidc_util_decode_json_and_check_error(r, *response, j_client) == FALSE) {
604 		oidc_error(r,
605 				"JSON parsing of dynamic client registration response failed");
606 		return FALSE;
607 	}
608 
609 	return TRUE;
610 }
611 
612 /*
613  * helper function to get the JWKs for the specified issuer
614  */
oidc_metadata_jwks_retrieve_and_cache(request_rec * r,oidc_cfg * cfg,const oidc_jwks_uri_t * jwks_uri,json_t ** j_jwks)615 static apr_byte_t oidc_metadata_jwks_retrieve_and_cache(request_rec *r,
616 		oidc_cfg *cfg, const oidc_jwks_uri_t *jwks_uri, json_t **j_jwks) {
617 
618 	char *response = NULL;
619 
620 	/* no valid provider metadata, get it at the specified URL with the specified parameters */
621 	if (oidc_util_http_get(r, jwks_uri->url, NULL, NULL,
622 			NULL, jwks_uri->ssl_validate_server, &response, cfg->http_timeout_long,
623 			cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r), NULL,
624 			NULL) == FALSE)
625 		return FALSE;
626 
627 	/* decode and see if it is not an error response somehow */
628 	if (oidc_util_decode_json_and_check_error(r, response, j_jwks) == FALSE) {
629 		oidc_error(r, "JSON parsing of JWKs published at the jwks_uri failed");
630 		return FALSE;
631 	}
632 
633 	/* check to see if it is valid metadata */
634 	if (oidc_metadata_jwks_is_valid(r, jwks_uri, *j_jwks) == FALSE)
635 		return FALSE;
636 
637 	/* store the JWKs in the cache */
638 	oidc_cache_set_jwks(r, oidc_metadata_jwks_cache_key(r, jwks_uri->url),
639 			response,
640 			apr_time_now() + apr_time_from_sec(jwks_uri->refresh_interval));
641 
642 	return TRUE;
643 }
644 
645 /*
646  * return JWKs for the specified issuer
647  */
oidc_metadata_jwks_get(request_rec * r,oidc_cfg * cfg,const oidc_jwks_uri_t * jwks_uri,json_t ** j_jwks,apr_byte_t * refresh)648 apr_byte_t oidc_metadata_jwks_get(request_rec *r, oidc_cfg *cfg,
649 		const oidc_jwks_uri_t *jwks_uri, json_t **j_jwks, apr_byte_t *refresh) {
650 
651 	oidc_debug(r, "enter, jwks_uri=%s, refresh=%d", jwks_uri->url, *refresh);
652 
653 	/* see if we need to do a forced refresh */
654 	if (*refresh == TRUE) {
655 		oidc_debug(r, "doing a forced refresh of the JWKs from URI \"%s\"",
656 				jwks_uri->url);
657 		if (oidc_metadata_jwks_retrieve_and_cache(r, cfg, jwks_uri,
658 				j_jwks) == TRUE)
659 			return TRUE;
660 		// else: fallback on any cached JWKs
661 	}
662 
663 	/* see if the JWKs is cached */
664 	char *value = NULL;
665 	oidc_cache_get_jwks(r, oidc_metadata_jwks_cache_key(r, jwks_uri->url),
666 			&value);
667 
668 	if (value == NULL) {
669 		/* it is non-existing or expired: do a forced refresh */
670 		*refresh = TRUE;
671 		return oidc_metadata_jwks_retrieve_and_cache(r, cfg, jwks_uri, j_jwks);
672 	}
673 
674 	/* decode and see if it is not an error response somehow */
675 	if (oidc_util_decode_json_and_check_error(r, value, j_jwks) == FALSE) {
676 		oidc_error(r, "JSON parsing of cached JWKs data failed");
677 		return FALSE;
678 	}
679 
680 	return TRUE;
681 }
682 
683 /*
684  * use OpenID Connect Discovery to get metadata for the specified issuer
685  */
oidc_metadata_provider_retrieve(request_rec * r,oidc_cfg * cfg,const char * issuer,const char * url,json_t ** j_metadata,char ** response)686 apr_byte_t oidc_metadata_provider_retrieve(request_rec *r, oidc_cfg *cfg,
687 		const char *issuer, const char *url, json_t **j_metadata,
688 		char **response) {
689 
690 	/* get provider metadata from the specified URL with the specified parameters */
691 	if (oidc_util_http_get(r, url, NULL, NULL, NULL,
692 			cfg->provider.ssl_validate_server, response,
693 			cfg->http_timeout_short, cfg->outgoing_proxy,
694 			oidc_dir_cfg_pass_cookies(r),
695 			NULL, NULL) == FALSE)
696 		return FALSE;
697 
698 	/* decode and see if it is not an error response somehow */
699 	if (oidc_util_decode_json_and_check_error(r, *response, j_metadata) == FALSE) {
700 		oidc_error(r, "JSON parsing of retrieved Discovery document failed");
701 		return FALSE;
702 	}
703 
704 	/* check to see if it is valid metadata */
705 	if (oidc_metadata_provider_is_valid(r, cfg, *j_metadata, issuer) == FALSE)
706 		return FALSE;
707 
708 	/* all OK */
709 	return TRUE;
710 }
711 
712 /*
713  * see if we have provider metadata and check its validity
714  * if not, use OpenID Connect Discovery to get it, check it and store it
715  */
oidc_metadata_provider_get(request_rec * r,oidc_cfg * cfg,const char * issuer,json_t ** j_provider,apr_byte_t allow_discovery)716 static apr_byte_t oidc_metadata_provider_get(request_rec *r, oidc_cfg *cfg,
717 		const char *issuer, json_t **j_provider, apr_byte_t allow_discovery) {
718 
719 	/* holds the response data/string/JSON from the OP */
720 	char *response = NULL;
721 
722 	/* get the full file path to the provider metadata for this issuer */
723 	const char *provider_path = oidc_metadata_provider_file_path(r, issuer);
724 
725 	/* check the last-modified timestamp */
726 	apr_byte_t use_cache = TRUE;
727 	apr_finfo_t fi;
728 	json_t *j_cache = NULL;
729 	apr_byte_t have_cache = FALSE;
730 
731 	/* see if we are refreshing metadata and we need a refresh */
732 	if (cfg->provider_metadata_refresh_interval > 0) {
733 
734 		have_cache = (apr_stat(&fi, provider_path, APR_FINFO_MTIME, r->pool)
735 				== APR_SUCCESS);
736 
737 		if (have_cache == TRUE)
738 			use_cache = (apr_time_now()
739 					< fi.mtime
740 					+ apr_time_from_sec(
741 							cfg->provider_metadata_refresh_interval));
742 
743 		oidc_debug(r, "use_cache: %s", use_cache ? "yes" : "no");
744 	}
745 
746 	/* see if we have valid metadata already, if so, return it */
747 	if (oidc_metadata_file_read_json(r, provider_path, &j_cache) == TRUE) {
748 
749 		/* return the validation result */
750 		if (use_cache == TRUE) {
751 			*j_provider = j_cache;
752 			return oidc_metadata_provider_is_valid(r, cfg, *j_provider, issuer);
753 		}
754 	}
755 
756 	if ((have_cache == FALSE) && (!allow_discovery)) {
757 		oidc_warn(r,
758 				"no metadata found for the requested issuer (%s), and Discovery is not allowed",
759 				issuer);
760 		return FALSE;
761 	}
762 
763 	/* assemble the URL to the .well-known OpenID metadata */
764 	const char *url = apr_psprintf(r->pool, "%s",
765 			((strstr(issuer, "http://") == issuer)
766 					|| (strstr(issuer, "https://") == issuer)) ?
767 							issuer : apr_psprintf(r->pool, "https://%s", issuer));
768 	url = apr_psprintf(r->pool, "%s%s.well-known/openid-configuration", url,
769 			url[strlen(url) - 1] != OIDC_CHAR_FORWARD_SLASH ?
770 					OIDC_STR_FORWARD_SLASH :
771 					"");
772 
773 	/* get the metadata for the issuer using OpenID Connect Discovery and validate it */
774 	if (oidc_metadata_provider_retrieve(r, cfg, issuer, url, j_provider,
775 			&response) == FALSE) {
776 
777 		oidc_debug(r,
778 				"could not retrieve provider metadata; have_cache: %s (data=%pp)",
779 				have_cache ? "yes" : "no", j_cache);
780 
781 		/* see if we can use at least the cache that may have expired by now */
782 		if ((cfg->provider_metadata_refresh_interval > 0)
783 				&& (have_cache == TRUE) && (j_cache != NULL)) {
784 
785 			/* reset the file-modified timestamp so it is cached for a while again */
786 			apr_file_mtime_set(provider_path, apr_time_now(), r->pool);
787 
788 			/* return the validated cached data */
789 			*j_provider = j_cache;
790 			return oidc_metadata_provider_is_valid(r, cfg, *j_provider, issuer);
791 		}
792 
793 		return FALSE;
794 	}
795 
796 	/* since it is valid, write the obtained provider metadata file */
797 	if (oidc_util_file_write(r, provider_path, response) == FALSE)
798 		return FALSE;
799 
800 	return TRUE;
801 }
802 
803 /*
804  * see if we have config metadata
805  */
oidc_metadata_conf_get(request_rec * r,oidc_cfg * cfg,const char * issuer,json_t ** j_conf)806 static apr_byte_t oidc_metadata_conf_get(request_rec *r, oidc_cfg *cfg,
807 		const char *issuer, json_t **j_conf) {
808 
809 	/* get the full file path to the conf metadata for this issuer */
810 	const char *conf_path = oidc_metadata_conf_path(r, issuer);
811 
812 	/* the .conf file is optional */
813 	apr_finfo_t fi;
814 	if (apr_stat(&fi, conf_path, APR_FINFO_MTIME, r->pool) != APR_SUCCESS)
815 		return TRUE;
816 
817 	/* see if we have valid metadata already, if so, return it */
818 	if (oidc_metadata_file_read_json(r, conf_path, j_conf) == TRUE) {
819 
820 		/* return the validation result */
821 		return oidc_metadata_conf_is_valid(r, *j_conf, issuer);
822 	}
823 
824 	return FALSE;
825 }
826 
827 /*
828  * see if we have client metadata and check its validity
829  * if not, use OpenID Connect Client Registration to get it, check it and store it
830  */
oidc_metadata_client_get(request_rec * r,oidc_cfg * cfg,const char * issuer,oidc_provider_t * provider,json_t ** j_client)831 static apr_byte_t oidc_metadata_client_get(request_rec *r, oidc_cfg *cfg,
832 		const char *issuer, oidc_provider_t *provider, json_t **j_client) {
833 
834 	/* get the full file path to the client metadata for this issuer */
835 	const char *client_path = oidc_metadata_client_file_path(r, issuer);
836 
837 	/* see if we have valid metadata already, if so, return it */
838 	if (oidc_metadata_file_read_json(r, client_path, j_client) == TRUE) {
839 
840 		/* if the client metadata is (still) valid, return it */
841 		if (oidc_metadata_client_is_valid(r, *j_client, issuer) == TRUE)
842 			return TRUE;
843 	}
844 
845 	/* at this point we have no valid client metadata, see if there's a registration endpoint for this provider */
846 	if (provider->registration_endpoint_url == NULL) {
847 		oidc_error(r,
848 				"no (valid) client metadata exists for provider (%s) and provider JSON object did not contain a (valid) \"" OIDC_METADATA_REGISTRATION_ENDPOINT "\" string",
849 				issuer);
850 		return FALSE;
851 	}
852 
853 	/* try and get client metadata by registering the client at the registration endpoint */
854 	char *response = NULL;
855 	if (oidc_metadata_client_register(r, cfg, provider, j_client,
856 			&response) == FALSE)
857 		return FALSE;
858 
859 	/* check to see if it is valid metadata */
860 	if (oidc_metadata_client_is_valid(r, *j_client, issuer) == FALSE)
861 		return FALSE;
862 
863 	/* since it is valid, write the obtained client metadata file */
864 	if (oidc_util_file_write(r, client_path, response) == FALSE)
865 		return FALSE;
866 
867 	return TRUE;
868 }
869 
870 /*
871  * get a list of configured OIDC providers based on the entries in the provider metadata directory
872  */
oidc_metadata_list(request_rec * r,oidc_cfg * cfg,apr_array_header_t ** list)873 apr_byte_t oidc_metadata_list(request_rec *r, oidc_cfg *cfg,
874 		apr_array_header_t **list) {
875 	apr_status_t rc;
876 	apr_dir_t *dir;
877 	apr_finfo_t fi;
878 	char s_err[128];
879 
880 	oidc_debug(r, "enter");
881 
882 	/* open the metadata directory */
883 	if ((rc = apr_dir_open(&dir, cfg->metadata_dir, r->pool)) != APR_SUCCESS) {
884 		oidc_error(r, "error opening metadata directory '%s' (%s)",
885 				cfg->metadata_dir, apr_strerror(rc, s_err, sizeof(s_err)));
886 		return FALSE;
887 	}
888 
889 	/* allocate some space in the array that will hold the list of providers */
890 	*list = apr_array_make(r->pool, 5, sizeof(const char*));
891 	/* BTW: we could estimate the number in the array based on # directory entries... */
892 
893 	/* loop over the entries in the provider metadata directory */
894 	while (apr_dir_read(&fi, APR_FINFO_NAME, dir) == APR_SUCCESS) {
895 
896 		/* skip "." and ".." entries */
897 		if (fi.name[0] == OIDC_CHAR_DOT)
898 			continue;
899 		/* skip other non-provider entries */
900 		char *ext = strrchr(fi.name, OIDC_CHAR_DOT);
901 		if ((ext == NULL)
902 				|| (strcmp(++ext, OIDC_METADATA_SUFFIX_PROVIDER) != 0))
903 			continue;
904 
905 		/* get the issuer from the filename */
906 		const char *issuer = oidc_metadata_filename_to_issuer(r, fi.name);
907 
908 		/* get the provider and client metadata, do all checks and registration if possible */
909 		oidc_provider_t *provider = NULL;
910 		if (oidc_metadata_get(r, cfg, issuer, &provider, FALSE) == TRUE) {
911 			/* push the decoded issuer filename in to the array */
912 			*(const char**) apr_array_push(*list) = provider->issuer;
913 		}
914 	}
915 
916 	/* we're done, cleanup now */
917 	apr_dir_close(dir);
918 
919 	return TRUE;
920 }
921 
922 /*
923  * parse boolean value from JSON configuration
924  */
oidc_metadata_parse_boolean(request_rec * r,json_t * json,const char * key,int * value,int default_value)925 static void oidc_metadata_parse_boolean(request_rec *r, json_t *json,
926 		const char *key, int *value, int default_value) {
927 	int int_value = 0;
928 	char *s_value = NULL;
929 	if (oidc_json_object_get_bool(r->pool, json, key, &int_value,
930 			default_value) == FALSE) {
931 		oidc_json_object_get_string(r->pool, json, key, &s_value,
932 				NULL);
933 		if (s_value != NULL) {
934 			const char *rv = oidc_parse_boolean(r->pool, s_value, &int_value);
935 			if (rv != NULL) {
936 				oidc_warn(r, "%s: %s", key, rv);
937 				int_value = default_value;
938 			}
939 		} else {
940 			oidc_json_object_get_int(r->pool, json, key, &int_value,
941 					default_value);
942 		}
943 	}
944 	*value = (int_value != 0) ? TRUE : FALSE;
945 }
946 
947 /*
948  * parse URL value from JSON configuration
949  */
oidc_metadata_parse_url(request_rec * r,const char * type,const char * issuer,json_t * json,const char * key,char ** value,const char * default_value)950 static void oidc_metadata_parse_url(request_rec *r, const char *type,
951 		const char *issuer, json_t *json, const char *key, char **value,
952 		const char *default_value) {
953 	if ((oidc_metadata_is_valid_uri(r, type, issuer, json, key, value,
954 			FALSE) == FALSE) || ((*value == NULL) && (default_value != NULL))) {
955 		*value = apr_pstrdup(r->pool, default_value);
956 	}
957 }
958 
959 /*
960  * parse the JSON provider metadata in to a oidc_provider_t struct but do not override values already set
961  */
oidc_metadata_provider_parse(request_rec * r,oidc_cfg * cfg,json_t * j_provider,oidc_provider_t * provider)962 apr_byte_t oidc_metadata_provider_parse(request_rec *r, oidc_cfg *cfg,
963 		json_t *j_provider, oidc_provider_t *provider) {
964 
965 	if (provider->issuer == NULL) {
966 		/* get the "issuer" from the provider metadata */
967 		oidc_json_object_get_string(r->pool, j_provider, OIDC_METADATA_ISSUER,
968 				&provider->issuer, NULL);
969 	}
970 
971 	if (provider->authorization_endpoint_url == NULL) {
972 		/* get a handle to the authorization endpoint */
973 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
974 				provider->issuer, j_provider,
975 				OIDC_METADATA_AUTHORIZATION_ENDPOINT,
976 				&provider->authorization_endpoint_url,
977 				NULL);
978 	}
979 
980 	if (provider->token_endpoint_url == NULL) {
981 		/* get a handle to the token endpoint */
982 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
983 				provider->issuer, j_provider,
984 				OIDC_METADATA_TOKEN_ENDPOINT, &provider->token_endpoint_url,
985 				NULL);
986 	}
987 
988 	if (provider->userinfo_endpoint_url == NULL) {
989 		/* get a handle to the user_info endpoint */
990 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
991 				provider->issuer, j_provider,
992 				OIDC_METADATA_USERINFO_ENDPOINT,
993 				&provider->userinfo_endpoint_url, NULL);
994 	}
995 
996 	if (provider->revocation_endpoint_url == NULL) {
997 		/* get a handle to the token revocation endpoint */
998 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
999 				provider->issuer, j_provider,
1000 				OIDC_METADATA_REVOCATION_ENDPOINT,
1001 				&provider->revocation_endpoint_url, NULL);
1002 	}
1003 
1004 	if (provider->jwks_uri == NULL) {
1005 		/* get a handle to the jwks_uri endpoint */
1006 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
1007 				provider->issuer, j_provider,
1008 				OIDC_METADATA_JWKS_URI, &provider->jwks_uri,
1009 				NULL);
1010 	}
1011 
1012 	if (provider->registration_endpoint_url == NULL) {
1013 		/* get a handle to the client registration endpoint */
1014 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
1015 				provider->issuer, j_provider,
1016 				OIDC_METADATA_REGISTRATION_ENDPOINT,
1017 				&provider->registration_endpoint_url,
1018 				NULL);
1019 	}
1020 
1021 	if (provider->check_session_iframe == NULL) {
1022 		/* get a handle to the check session iframe */
1023 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
1024 				provider->issuer, j_provider,
1025 				OIDC_METADATA_CHECK_SESSION_IFRAME,
1026 				&provider->check_session_iframe, NULL);
1027 	}
1028 
1029 	if (provider->end_session_endpoint == NULL) {
1030 		/* get a handle to the end session endpoint */
1031 		oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER,
1032 				provider->issuer, j_provider,
1033 				OIDC_METADATA_END_SESSION_ENDPOINT,
1034 				&provider->end_session_endpoint, NULL);
1035 	}
1036 
1037 	// TODO: default 0 should have a defined default that may be 1...
1038 	if (provider->backchannel_logout_supported == OIDC_CONFIG_POS_INT_UNSET) {
1039 		oidc_metadata_parse_boolean(r, j_provider,
1040 				OIDC_METADATA_BACKCHANNEL_LOGOUT_SUPPORTED,
1041 				&provider->backchannel_logout_supported, 0);
1042 	}
1043 
1044 	if (provider->token_endpoint_auth == NULL) {
1045 		if (oidc_valid_string_in_array(r->pool, j_provider,
1046 				OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,
1047 				oidc_cfg_get_valid_endpoint_auth_function(cfg),
1048 				&provider->token_endpoint_auth,
1049 				TRUE, OIDC_ENDPOINT_AUTH_CLIENT_SECRET_BASIC) != NULL) {
1050 			oidc_error(r,
1051 					"could not find a supported token endpoint authentication method in provider metadata (%s) for entry \"" OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED "\"",
1052 					provider->issuer);
1053 			return FALSE;
1054 		}
1055 	}
1056 
1057 	return TRUE;
1058 }
1059 
oidc_oauth_metadata_provider_parse(request_rec * r,oidc_cfg * c,json_t * j_provider)1060 apr_byte_t oidc_oauth_metadata_provider_parse(request_rec *r, oidc_cfg *c,
1061 		json_t *j_provider) {
1062 
1063 	char *issuer = NULL;
1064 
1065 	/* get the "issuer" from the provider metadata */
1066 	oidc_json_object_get_string(r->pool, j_provider, OIDC_METADATA_ISSUER,
1067 			&issuer, NULL);
1068 
1069 	// TOOD: should check for "if c->oauth.introspection_endpoint_url == NULL and
1070 	//       allocate the string from the process/config pool
1071 	//
1072 	// https://github.com/zmartzone/mod_auth_openidc/commit/32321024ed5bdbc02ba8b5d61aabc4a4c3745c89
1073 	// https://groups.google.com/forum/#!topic/mod_auth_openidc/o1K_1Yh-TQA
1074 
1075 	/* get a handle to the introspection endpoint */
1076 	oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER, issuer,
1077 			j_provider,
1078 			OIDC_METADATA_INTROSPECTION_ENDPOINT,
1079 			&c->oauth.introspection_endpoint_url,
1080 			NULL);
1081 
1082 	/* get a handle to the jwks_uri endpoint */
1083 	oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_PROVIDER, issuer,
1084 			j_provider,
1085 			OIDC_METADATA_JWKS_URI, &c->oauth.verify_jwks_uri,
1086 			NULL);
1087 
1088 	if (oidc_valid_string_in_array(r->pool, j_provider,
1089 			OIDC_METADATA_INTROSPECTON_ENDPOINT_AUTH_METHODS_SUPPORTED,
1090 			oidc_cfg_get_valid_endpoint_auth_function(c),
1091 			&c->oauth.introspection_endpoint_auth,
1092 			TRUE, OIDC_ENDPOINT_AUTH_CLIENT_SECRET_BASIC) != NULL) {
1093 		oidc_error(r,
1094 				"could not find a supported token endpoint authentication method in provider metadata (%s) for entry \"" OIDC_METADATA_INTROSPECTON_ENDPOINT_AUTH_METHODS_SUPPORTED "\"",
1095 				issuer);
1096 		return FALSE;
1097 	}
1098 
1099 	return TRUE;
1100 }
1101 
1102 /*
1103  * get a string value from a JSON object and see if it is a valid value according to the specified validation function
1104  */
oidc_metadata_get_valid_string(request_rec * r,json_t * json,const char * key,oidc_valid_function_t valid_function,char ** str_value,const char * default_str_value)1105 void oidc_metadata_get_valid_string(request_rec *r, json_t *json,
1106 		const char *key, oidc_valid_function_t valid_function, char **str_value,
1107 		const char *default_str_value) {
1108 	char *v = NULL;
1109 	oidc_json_object_get_string(r->pool, json, key, &v, default_str_value);
1110 	if (v != NULL) {
1111 		const char *rv = valid_function(r->pool, v);
1112 		if (rv != NULL) {
1113 			oidc_warn(r,
1114 					"string value %s for key \"%s\" is invalid: %s; using default: %s",
1115 					v, key, rv, default_str_value);
1116 			v = apr_pstrdup(r->pool, default_str_value);
1117 		}
1118 	}
1119 	*str_value = v;
1120 }
1121 
1122 /*
1123  * get an integer value from a JSON object and see if it is a valid value according to the specified validation function
1124  */
oidc_metadata_get_valid_int(request_rec * r,json_t * json,const char * key,oidc_valid_int_function_t valid_int_function,int * int_value,int default_int_value)1125 void oidc_metadata_get_valid_int(request_rec *r, json_t *json, const char *key,
1126 		oidc_valid_int_function_t valid_int_function, int *int_value,
1127 		int default_int_value) {
1128 	int v = 0;
1129 	oidc_json_object_get_int(r->pool, json, key, &v, default_int_value);
1130 	const char *rv = valid_int_function(r->pool, v);
1131 	if (rv != NULL) {
1132 		oidc_warn(r,
1133 				"integer value %d for key \"%s\" is invalid: %s; using default: %d",
1134 				v, key, rv, default_int_value);
1135 		v = default_int_value;
1136 	}
1137 	*int_value = v;
1138 }
1139 
oidc_metadata_get_jwks(request_rec * r,json_t * json,const char * s_use,apr_array_header_t ** jwk_list)1140 static void oidc_metadata_get_jwks(request_rec *r, json_t *json,
1141 		const char *s_use, apr_array_header_t **jwk_list) {
1142 	json_t *keys = NULL;
1143 	int i = 0;
1144 	oidc_jose_error_t err;
1145 	oidc_jwk_t *jwk = NULL;
1146 	json_t *elem = NULL;
1147 	const char *use = NULL;
1148 
1149 	keys = json_object_get(json, OIDC_JWK_KEYS);
1150 	if (keys == NULL)
1151 		return;
1152 
1153 	if (!json_is_array(keys)) {
1154 		oidc_error(r,
1155 				"trying to parse a list of JWKs but the value for key \"%s\" is not a JSON array",
1156 				OIDC_JWK_KEYS);
1157 		return;
1158 	}
1159 
1160 	for (i = 0; i < json_array_size(keys); i++) {
1161 
1162 		elem = json_array_get(keys, i);
1163 
1164 		use = json_string_value(json_object_get(elem, OIDC_JWK_USE));
1165 		if ((use != NULL) && (strcmp(use, s_use) != 0)) {
1166 			oidc_debug(r,
1167 					"skipping key because of non-matching \"%s\": \"%s\" != \"%s\"",
1168 					OIDC_JWK_USE, use, s_use);
1169 			continue;
1170 		}
1171 
1172 		if (oidc_jwk_parse_json(r->pool, elem, &jwk, &err) == FALSE) {
1173 			oidc_warn(r, "oidc_jwk_parse_json failed: %s",
1174 					oidc_jose_e2s(r->pool, err));
1175 			continue;
1176 		}
1177 
1178 		if (*jwk_list == NULL)
1179 			*jwk_list = apr_array_make(r->pool, 4, sizeof(const oidc_jwk_t*));
1180 		*(const oidc_jwk_t**) apr_array_push(*jwk_list) = jwk;
1181 	}
1182 }
1183 
1184 /*
1185  * parse the JSON conf metadata in to a oidc_provider_t struct
1186  */
oidc_metadata_conf_parse(request_rec * r,oidc_cfg * cfg,json_t * j_conf,oidc_provider_t * provider)1187 apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg *cfg,
1188 		json_t *j_conf, oidc_provider_t *provider) {
1189 
1190 	oidc_metadata_parse_url(r, OIDC_METADATA_SUFFIX_CONF, provider->issuer,
1191 			j_conf,
1192 			OIDC_METADATA_CLIENT_JWKS_URI, &provider->client_jwks_uri,
1193 			cfg->provider.client_jwks_uri);
1194 
1195 	oidc_metadata_get_jwks(r, j_conf,
1196 			OIDC_JWK_SIG, &provider->client_signing_keys);
1197 	oidc_metadata_get_jwks(r, j_conf,
1198 			OIDC_JWK_ENC, &provider->client_encryption_keys);
1199 
1200 	/* get the (optional) signing & encryption settings for the id_token */
1201 	oidc_metadata_get_valid_string(r, j_conf,
1202 			OIDC_METADATA_ID_TOKEN_SIGNED_RESPONSE_ALG, oidc_valid_signed_response_alg,
1203 			&provider->id_token_signed_response_alg,
1204 			cfg->provider.id_token_signed_response_alg);
1205 	oidc_metadata_get_valid_string(r, j_conf,
1206 			OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ALG,
1207 			oidc_valid_encrypted_response_alg,
1208 			&provider->id_token_encrypted_response_alg,
1209 			cfg->provider.id_token_encrypted_response_alg);
1210 	oidc_metadata_get_valid_string(r, j_conf,
1211 			OIDC_METADATA_ID_TOKEN_ENCRYPTED_RESPONSE_ENC,
1212 			oidc_valid_encrypted_response_enc,
1213 			&provider->id_token_encrypted_response_enc,
1214 			cfg->provider.id_token_encrypted_response_enc);
1215 
1216 	/* get the (optional) signing & encryption settings for the userinfo response */
1217 	oidc_metadata_get_valid_string(r, j_conf,
1218 			OIDC_METADATA_USERINFO_SIGNED_RESPONSE_ALG, oidc_valid_signed_response_alg,
1219 			&provider->userinfo_signed_response_alg,
1220 			cfg->provider.userinfo_signed_response_alg);
1221 	oidc_metadata_get_valid_string(r, j_conf,
1222 			OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ALG,
1223 			oidc_valid_encrypted_response_alg,
1224 			&provider->userinfo_encrypted_response_alg,
1225 			cfg->provider.userinfo_encrypted_response_alg);
1226 	oidc_metadata_get_valid_string(r, j_conf,
1227 			OIDC_METADATA_USERINFO_ENCRYPTED_RESPONSE_ENC,
1228 			oidc_valid_encrypted_response_enc,
1229 			&provider->userinfo_encrypted_response_enc,
1230 			cfg->provider.userinfo_encrypted_response_enc);
1231 
1232 	/* find out if we need to perform SSL server certificate validation on the token_endpoint and user_info_endpoint for this provider */
1233 	oidc_metadata_parse_boolean(r, j_conf, OIDC_METADATA_SSL_VALIDATE_SERVER,
1234 			&provider->ssl_validate_server, cfg->provider.ssl_validate_server);
1235 
1236 	oidc_metadata_parse_boolean(r, j_conf, OIDC_METADATA_VALIDATE_ISSUER,
1237 			&provider->validate_issuer, cfg->provider.validate_issuer);
1238 
1239 	/* find out what scopes we should be requesting from this provider */
1240 	// TODO: use the provider "scopes_supported" to mix-and-match with what we've configured for the client
1241 	// TODO: check that "openid" is always included in the configured scopes, right?
1242 	oidc_json_object_get_string(r->pool, j_conf, OIDC_METADATA_SCOPE,
1243 			&provider->scope, cfg->provider.scope);
1244 
1245 	/* see if we've got a custom JWKs refresh interval */
1246 	oidc_metadata_get_valid_int(r, j_conf, OIDC_METADATA_JWKS_REFRESH_INTERVAL,
1247 			oidc_valid_jwks_refresh_interval, &provider->jwks_refresh_interval,
1248 			cfg->provider.jwks_refresh_interval);
1249 
1250 	/* see if we've got a custom IAT slack interval */
1251 	oidc_metadata_get_valid_int(r, j_conf, OIDC_METADATA_IDTOKEN_IAT_SLACK,
1252 			oidc_valid_idtoken_iat_slack, &provider->idtoken_iat_slack,
1253 			cfg->provider.idtoken_iat_slack);
1254 
1255 	/* see if we've got a custom max session duration */
1256 	oidc_metadata_get_valid_int(r, j_conf, OIDC_METADATA_SESSION_MAX_DURATION,
1257 			oidc_valid_session_max_duration, &provider->session_max_duration,
1258 			cfg->provider.session_max_duration);
1259 
1260 	/* see if we've got custom authentication request parameter values */
1261 	oidc_json_object_get_string(r->pool, j_conf,
1262 			OIDC_METADATA_AUTH_REQUEST_PARAMS, &provider->auth_request_params,
1263 			cfg->provider.auth_request_params);
1264 
1265 	/* see if we've got custom token endpoint parameter values */
1266 	oidc_json_object_get_string(r->pool, j_conf,
1267 			OIDC_METADATA_TOKEN_ENDPOINT_PARAMS, &provider->token_endpoint_params,
1268 			cfg->provider.token_endpoint_params);
1269 
1270 	/* get the response mode to use */
1271 	oidc_metadata_get_valid_string(r, j_conf, OIDC_METADATA_RESPONSE_MODE,
1272 			oidc_valid_response_mode, &provider->response_mode,
1273 			cfg->provider.response_mode);
1274 
1275 	/* get the PKCE method to use */
1276 	char *pkce_method = NULL;
1277 	oidc_metadata_get_valid_string(r, j_conf, OIDC_METADATA_PKCE_METHOD,
1278 			oidc_valid_pkce_method, &pkce_method,
1279 			cfg->provider.pkce ? cfg->provider.pkce->method : NULL);
1280 	if (pkce_method != NULL)
1281 		oidc_parse_pkce_type(r->pool, pkce_method, &provider->pkce);
1282 
1283 	/* get the client name */
1284 	oidc_json_object_get_string(r->pool, j_conf, OIDC_METADATA_CLIENT_NAME,
1285 			&provider->client_name, cfg->provider.client_name);
1286 
1287 	/* get the client contact */
1288 	oidc_json_object_get_string(r->pool, j_conf, OIDC_METADATA_CLIENT_CONTACT,
1289 			&provider->client_contact, cfg->provider.client_contact);
1290 
1291 	/* get the token endpoint authentication method */
1292 	oidc_metadata_get_valid_string(r, j_conf, OIDC_METADATA_TOKEN_ENDPOINT_AUTH,
1293 			oidc_cfg_get_valid_endpoint_auth_function(cfg),
1294 			&provider->token_endpoint_auth, provider->token_endpoint_auth);
1295 
1296 	/* get the dynamic client registration token */
1297 	oidc_json_object_get_string(r->pool, j_conf,
1298 			OIDC_METADATA_REGISTRATION_TOKEN, &provider->registration_token,
1299 			cfg->provider.registration_token);
1300 
1301 	/* see if we've got custom registration request parameter values */
1302 	oidc_json_object_get_string(r->pool, j_conf,
1303 			OIDC_METADATA_REGISTRATION_ENDPOINT_JSON,
1304 			&provider->registration_endpoint_json,
1305 			cfg->provider.registration_endpoint_json);
1306 
1307 	/* get the flow to use; let the .client file set it otherwise (pass NULL as default value) */
1308 	oidc_metadata_get_valid_string(r, j_conf, OIDC_METADATA_RESPONSE_TYPE,
1309 			oidc_valid_response_type, &provider->response_type,
1310 			NULL);
1311 
1312 	/* see if we've got a custom user info refresh interval */
1313 	oidc_metadata_get_valid_int(r, j_conf,
1314 			OIDC_METADATA_USERINFO_REFRESH_INTERVAL,
1315 			oidc_valid_userinfo_refresh_interval,
1316 			&provider->userinfo_refresh_interval,
1317 			cfg->provider.userinfo_refresh_interval);
1318 
1319 	/* TLS client cert auth settings */
1320 	oidc_json_object_get_string(r->pool, j_conf,
1321 			OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_CERT,
1322 			&provider->token_endpoint_tls_client_cert,
1323 			cfg->provider.token_endpoint_tls_client_cert);
1324 	oidc_json_object_get_string(r->pool, j_conf,
1325 			OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_KEY,
1326 			&provider->token_endpoint_tls_client_key,
1327 			cfg->provider.token_endpoint_tls_client_key);
1328 
1329 	oidc_json_object_get_string(r->pool, j_conf, OIDC_METADATA_REQUEST_OBJECT,
1330 			&provider->request_object, cfg->provider.request_object);
1331 
1332 	/* see if we've got a custom userinfo endpoint token presentation method */
1333 	char *method = NULL;
1334 	oidc_metadata_get_valid_string(r, j_conf,
1335 			OIDC_METADATA_USERINFO_TOKEN_METHOD, oidc_valid_userinfo_token_method,
1336 			&method,
1337 			NULL);
1338 	if (method != NULL)
1339 		oidc_parse_userinfo_token_method(r->pool, method,
1340 				&provider->userinfo_token_method);
1341 	else
1342 		provider->userinfo_token_method = OIDC_USER_INFO_TOKEN_METHOD_HEADER;
1343 
1344 	/* see if we've got a custom token binding policy */
1345 	char *policy = NULL;
1346 	oidc_metadata_get_valid_string(r, j_conf,
1347 			OIDC_METADATA_TOKEN_BINDING_POLICY, oidc_valid_token_binding_policy,
1348 			&policy,
1349 			NULL);
1350 	if (policy != NULL)
1351 		oidc_parse_token_binding_policy(r->pool, policy,
1352 				&provider->token_binding_policy);
1353 	else
1354 		provider->token_binding_policy = cfg->provider.token_binding_policy;
1355 
1356 	/* see if we've got a custom HTTP method for passing the auth request */
1357 	oidc_metadata_get_valid_string(r, j_conf, OIDC_METADATA_AUTH_REQUEST_METHOD,
1358 			oidc_valid_auth_request_method, &method,
1359 			NULL);
1360 	if (method != NULL)
1361 		oidc_parse_auth_request_method(r->pool, method,
1362 				&provider->auth_request_method);
1363 	else
1364 		provider->auth_request_method = cfg->provider.auth_request_method;
1365 
1366 	/* get the issuer specific redirect URI option */
1367 	oidc_metadata_parse_boolean(r, j_conf,
1368 			OIDC_METADATA_ISSUER_SPECIFIC_REDIRECT_URI,
1369 			&provider->issuer_specific_redirect_uri,
1370 			cfg->provider.issuer_specific_redirect_uri);
1371 
1372 	return TRUE;
1373 }
1374 
1375 /*
1376  * parse the JSON client metadata in to a oidc_provider_t struct
1377  */
oidc_metadata_client_parse(request_rec * r,oidc_cfg * cfg,json_t * j_client,oidc_provider_t * provider)1378 apr_byte_t oidc_metadata_client_parse(request_rec *r, oidc_cfg *cfg,
1379 		json_t *j_client, oidc_provider_t *provider) {
1380 
1381 	/* get a handle to the client_id we need to use for this provider */
1382 	oidc_json_object_get_string(r->pool, j_client, OIDC_METADATA_CLIENT_ID,
1383 			&provider->client_id, NULL);
1384 
1385 	/* get a handle to the client_secret we need to use for this provider */
1386 	oidc_json_object_get_string(r->pool, j_client, OIDC_METADATA_CLIENT_SECRET,
1387 			&provider->client_secret, NULL);
1388 
1389 	/* see if the token endpoint auth method defined in the client metadata overrides the provider one */
1390 	char *token_endpoint_auth = NULL;
1391 	oidc_json_object_get_string(r->pool, j_client,
1392 			OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHOD, &token_endpoint_auth,
1393 			NULL);
1394 
1395 	if (token_endpoint_auth != NULL) {
1396 		if (oidc_cfg_get_valid_endpoint_auth_function(cfg)(r->pool,
1397 				token_endpoint_auth) == NULL) {
1398 			provider->token_endpoint_auth = apr_pstrdup(r->pool,
1399 					token_endpoint_auth);
1400 		} else {
1401 			oidc_warn(r,
1402 					"unsupported client auth method \"%s\" in client metadata for entry \"" OIDC_METADATA_TOKEN_ENDPOINT_AUTH_METHOD "\"",
1403 					token_endpoint_auth);
1404 		}
1405 	}
1406 
1407 	/* determine the response type if not set by .conf */
1408 	if (provider->response_type == NULL) {
1409 
1410 		provider->response_type = cfg->provider.response_type;
1411 
1412 		/* "response_types" is an array in the client metadata as by spec */
1413 		json_t *j_response_types = json_object_get(j_client,
1414 				OIDC_METADATA_RESPONSE_TYPES);
1415 		if ((j_response_types != NULL) && (json_is_array(j_response_types))) {
1416 			/* if there's an array we'll prefer the configured response_type if supported */
1417 			if (oidc_util_json_array_has_value(r, j_response_types,
1418 					provider->response_type) == FALSE) {
1419 				/* if the configured response_type is not supported, we'll fallback to the first one that is listed */
1420 				json_t *j_response_type = json_array_get(j_response_types, 0);
1421 				if (json_is_string(j_response_type)) {
1422 					provider->response_type = apr_pstrdup(r->pool,
1423 							json_string_value(j_response_type));
1424 				}
1425 			}
1426 		}
1427 	}
1428 
1429 	return TRUE;
1430 }
1431 
1432 /*
1433  * get the metadata for a specified issuer
1434  *
1435  * this fill the oidc_provider_t struct based on the issuer filename by reading and merging
1436  * contents from both provider metadata directory and client metadata directory
1437  */
oidc_metadata_get(request_rec * r,oidc_cfg * cfg,const char * issuer,oidc_provider_t ** provider,apr_byte_t allow_discovery)1438 apr_byte_t oidc_metadata_get(request_rec *r, oidc_cfg *cfg, const char *issuer,
1439 		oidc_provider_t **provider, apr_byte_t allow_discovery) {
1440 
1441 	apr_byte_t rc = FALSE;
1442 
1443 	/* pointers to the parsed JSON metadata */
1444 	json_t *j_provider = NULL;
1445 	json_t *j_client = NULL;
1446 	json_t *j_conf = NULL;
1447 
1448 	/* allocate space for a parsed-and-merged metadata struct */
1449 	*provider = apr_pcalloc(r->pool, sizeof(oidc_provider_t));
1450 	oidc_cfg_provider_init(*provider);
1451 
1452 	/*
1453 	 * read and parse the provider, conf and client metadata respectively
1454 	 * NB: order is important here
1455 	 */
1456 
1457 	if (oidc_metadata_provider_get(r, cfg, issuer, &j_provider,
1458 			allow_discovery) == FALSE)
1459 		goto end;
1460 	if (oidc_metadata_provider_parse(r, cfg, j_provider, *provider) == FALSE)
1461 		goto end;
1462 
1463 	if (oidc_metadata_conf_get(r, cfg, issuer, &j_conf) == FALSE)
1464 		goto end;
1465 	if (oidc_metadata_conf_parse(r, cfg, j_conf, *provider) == FALSE)
1466 		goto end;
1467 
1468 	if (oidc_metadata_client_get(r, cfg, issuer, *provider, &j_client) == FALSE)
1469 		goto end;
1470 	if (oidc_metadata_client_parse(r, cfg, j_client, *provider) == FALSE)
1471 		goto end;
1472 
1473 	rc = TRUE;
1474 
1475 end:
1476 
1477 	if (j_provider)
1478 		json_decref(j_provider);
1479 	if (j_conf)
1480 		json_decref(j_conf);
1481 	if (j_client)
1482 		json_decref(j_client);
1483 
1484 	return rc;
1485 }
1486