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