~ubuntu-branches/ubuntu/vivid/libapache2-mod-auth-openidc/vivid-proposed

« back to all changes in this revision

Viewing changes to src/metadata.c

  • Committer: Package Import Robot
  • Author(s): Hans Zandbelt
  • Date: 2014-10-13 12:23:35 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20141013122335-31wgnq50ascmubib
Tags: 1.6.0-1
new upstream release; add libssl-dev dependency

Show diffs side-by-side

added added

removed removed

Lines of Context:
58
58
#include <httpd.h>
59
59
#include <http_log.h>
60
60
 
61
 
// for converting JWKs
62
 
#include <openssl/evp.h>
63
 
#include <openssl/rsa.h>
64
 
#include <openssl/err.h>
65
 
#include <openssl/pem.h>
66
 
 
67
61
#include "mod_auth_openidc.h"
68
62
 
69
63
extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
83
77
        if (p == issuer) {
84
78
                p = apr_pstrdup(r->pool, issuer + strlen("https://"));
85
79
        } else {
86
 
                p = apr_pstrdup(r->pool, issuer);
 
80
                p = strstr(issuer, "http://");
 
81
                if (p == issuer) {
 
82
                        p = apr_pstrdup(r->pool, issuer + strlen("http://"));
 
83
                } else {
 
84
                        p = apr_pstrdup(r->pool, issuer);
 
85
                }
87
86
        }
88
87
 
89
88
        /* strip trailing '/' */
124
123
        oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
125
124
                        &auth_openidc_module);
126
125
        return oidc_metadata_file_path(r, cfg, issuer,
127
 
        OIDC_METADATA_SUFFIX_PROVIDER);
 
126
                        OIDC_METADATA_SUFFIX_PROVIDER);
128
127
}
129
128
 
130
129
/*
171
170
 
172
171
        if (*result == NULL) {
173
172
                /* something went wrong */
174
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
175
 
                                "oidc_metadata_file_read_json: JSON parsing (%s) returned an error: %s",
176
 
                                path, json_error.text);
 
173
                oidc_error(r, "JSON parsing (%s) returned an error: %s", path,
 
174
                                json_error.text);
177
175
                return FALSE;
178
176
        }
179
177
 
180
178
        if (!json_is_object(*result)) {
181
179
                /* oops, no JSON */
182
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
183
 
                                "oidc_metadata_file_read_json: parsed JSON from (%s) did not contain a JSON object",
 
180
                oidc_error(r, "parsed JSON from (%s) did not contain a JSON object",
184
181
                                path);
185
182
                json_decref(*result);
186
183
                return FALSE;
187
184
        }
188
185
 
189
 
        /* log successful metadata retrieval */
190
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
191
 
                        "oidc_metadata_file_read_json: JSON parsed from file \"%s\"", path);
 
186
        return TRUE;
 
187
}
 
188
 
 
189
/*
 
190
 * check if the specified entry in metadata is a valid URI
 
191
 */
 
192
static apr_byte_t oidc_metadata_is_valid_uri(request_rec *r, const char *type,
 
193
                const char *issuer, const json_t *json, const char *key,
 
194
                apr_byte_t is_mandatory) {
 
195
 
 
196
        apr_uri_t uri;
 
197
        json_t *entry = NULL;
 
198
 
 
199
        entry = json_object_get(json, key);
 
200
 
 
201
        if (entry == NULL) {
 
202
                if (is_mandatory) {
 
203
                        oidc_error(r,
 
204
                                        "%s (%s) JSON metadata does not contain the mandatory \"%s\" entry",
 
205
                                        type, issuer, key);
 
206
                }
 
207
                return (!is_mandatory);
 
208
        }
 
209
 
 
210
        if (!json_is_string(entry)) {
 
211
                oidc_error(r,
 
212
                                "%s (%s) JSON metadata contains a \"%s\" entry, but it is not a string value",
 
213
                                type, issuer, key);
 
214
                return FALSE;
 
215
        }
 
216
 
 
217
        if (apr_uri_parse(r->pool, json_string_value(entry), &uri) != APR_SUCCESS) {
 
218
                oidc_error(r,
 
219
                                "%s (%s) JSON metadata contains a \"%s\" entry, but it is not a valid URI",
 
220
                                type, issuer, key);
 
221
                return FALSE;
 
222
        }
192
223
 
193
224
        return TRUE;
194
225
}
202
233
        /* get the "issuer" from the provider metadata and double-check that it matches what we looked for */
203
234
        json_t *j_issuer = json_object_get(j_provider, "issuer");
204
235
        if ((j_issuer == NULL) || (!json_is_string(j_issuer))) {
205
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
206
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain an \"issuer\" string",
 
236
                oidc_error(r,
 
237
                                "provider (%s) JSON metadata did not contain an \"issuer\" string",
207
238
                                issuer);
208
239
                return FALSE;
209
240
        }
210
241
 
211
242
        /* check that the issuer matches */
212
 
        if (oidc_util_issuer_match(issuer, json_string_value(j_issuer)) == FALSE) {
213
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
214
 
                                "oidc_metadata_provider_is_valid: requested issuer (%s) does not match the \"issuer\" value in the provider metadata file: %s",
215
 
                                issuer, json_string_value(j_issuer));
216
 
                return FALSE;
 
243
        if (issuer != NULL) {
 
244
                if (oidc_util_issuer_match(issuer, json_string_value(j_issuer)) == FALSE) {
 
245
                        oidc_warn(r,
 
246
                                        "requested issuer (%s) does not match the \"issuer\" value in the provider metadata file: %s",
 
247
                                        issuer, json_string_value(j_issuer));
 
248
                        //return FALSE;
 
249
                }
217
250
        }
218
251
 
219
252
        /* verify that the provider supports the a flow that we implement */
225
258
                for (i = 0; i < json_array_size(j_response_types_supported); i++) {
226
259
                        json_t *elem = json_array_get(j_response_types_supported, i);
227
260
                        if (!json_is_string(elem)) {
228
 
                                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
229
 
                                                "oidc_metadata_provider_is_valid: unhandled in-array JSON non-string object type [%d]",
 
261
                                oidc_error(r,
 
262
                                                "unhandled in-array JSON non-string object type [%d]",
230
263
                                                elem->type);
231
264
                                continue;
232
265
                        }
234
267
                                break;
235
268
                }
236
269
                if (i == json_array_size(j_response_types_supported)) {
237
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
238
 
                                        "oidc_metadata_provider_is_valid: could not find a supported response type in provider metadata (%s) for entry \"response_types_supported\"; assuming that \"code\" flow is supported...",
 
270
                        oidc_warn(r,
 
271
                                        "could not find a supported response type in provider metadata (%s) for entry \"response_types_supported\"; assuming that \"code\" flow is supported...",
239
272
                                        issuer);
240
273
                        //return FALSE;
241
274
                }
242
275
        } else {
243
 
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
244
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain a \"response_types_supported\" array; assuming that \"code\" flow is supported...",
 
276
                oidc_warn(r,
 
277
                                "provider (%s) JSON metadata did not contain a \"response_types_supported\" array; assuming that \"code\" flow is supported...",
245
278
                                issuer);
246
279
                // TODO: hey, this is required-by-spec stuff right?
247
280
        }
255
288
                for (i = 0; i < json_array_size(response_modes_supported); i++) {
256
289
                        json_t *elem = json_array_get(response_modes_supported, i);
257
290
                        if (!json_is_string(elem)) {
258
 
                                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
259
 
                                                "oidc_metadata_provider_is_valid: unhandled in-array JSON non-string object type [%d]",
 
291
                                oidc_error(r,
 
292
                                                "unhandled in-array JSON non-string object type [%d]",
260
293
                                                elem->type);
261
294
                                continue;
262
295
                        }
266
299
                                break;
267
300
                }
268
301
                if (i == json_array_size(response_modes_supported)) {
269
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
270
 
                                        "oidc_metadata_provider_is_valid: could not find a supported response mode in provider metadata (%s) for entry \"response_modes_supported\"",
 
302
                        oidc_warn(r,
 
303
                                        "could not find a supported response mode in provider metadata (%s) for entry \"response_modes_supported\"",
271
304
                                        issuer);
272
305
                        return FALSE;
273
306
                }
274
307
        } else {
275
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
276
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain a \"response_modes_supported\" array; assuming that \"fragment\" and \"query\" are supported",
277
 
                                issuer);
278
 
        }
279
 
 
280
 
        /* get a handle to the authorization endpoint */
281
 
        json_t *j_authorization_endpoint = json_object_get(j_provider,
282
 
                        "authorization_endpoint");
283
 
        if ((j_authorization_endpoint == NULL)
284
 
                        || (!json_is_string(j_authorization_endpoint))) {
285
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
286
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain an \"authorization_endpoint\" string",
287
 
                                issuer);
288
 
                return FALSE;
289
 
        }
290
 
 
291
 
        /* get a handle to the token endpoint */
292
 
        json_t *j_token_endpoint = json_object_get(j_provider, "token_endpoint");
293
 
        if ((j_token_endpoint == NULL) || (!json_is_string(j_token_endpoint))) {
294
 
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
295
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain a \"token_endpoint\" string",
296
 
                                issuer);
297
 
                //return FALSE;
298
 
        }
299
 
 
300
 
        /* get a handle to the user_info endpoint */
301
 
        json_t *j_userinfo_endpoint = json_object_get(j_provider,
302
 
                        "userinfo_endpoint");
303
 
        if ((j_userinfo_endpoint != NULL)
304
 
                        && (!json_is_string(j_userinfo_endpoint))) {
305
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
306
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata contains a \"userinfo_endpoint\" entry, but it is not a string value",
307
 
                                issuer);
308
 
        }
309
 
        // TODO: check for valid URL
310
 
 
311
 
        /* get a handle to the jwks_uri */
312
 
        json_t *j_jwks_uri = json_object_get(j_provider, "jwks_uri");
313
 
        if ((j_jwks_uri == NULL) || (!json_is_string(j_jwks_uri))) {
314
 
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
315
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain a \"jwks_uri\" string",
316
 
                                issuer);
317
 
                //return FALSE;
318
 
        }
 
308
                oidc_debug(r,
 
309
                                "provider (%s) JSON metadata did not contain a \"response_modes_supported\" array; assuming that \"fragment\" and \"query\" are supported",
 
310
                                issuer);
 
311
        }
 
312
 
 
313
        /* check the required authorization endpoint */
 
314
        if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider,
 
315
                        "authorization_endpoint", TRUE) == FALSE)
 
316
                return FALSE;
 
317
 
 
318
        /* check the optional token endpoint */
 
319
        if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider,
 
320
                        "token_endpoint", FALSE) == FALSE)
 
321
                return FALSE;
 
322
 
 
323
        /* check the optional user info endpoint */
 
324
        if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider,
 
325
                        "userinfo_endpoint", FALSE) == FALSE)
 
326
                return FALSE;
 
327
 
 
328
        /* check the optional JWKs URI */
 
329
        if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider,
 
330
                        "jwks_uri", FALSE) == FALSE)
 
331
                return FALSE;
319
332
 
320
333
        /* find out what type of authentication the token endpoint supports (we only support post or basic) */
321
334
        json_t *j_token_endpoint_auth_methods_supported = json_object_get(
322
335
                        j_provider, "token_endpoint_auth_methods_supported");
323
336
        if ((j_token_endpoint_auth_methods_supported == NULL)
324
337
                        || (!json_is_array(j_token_endpoint_auth_methods_supported))) {
325
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
326
 
                                "oidc_metadata_provider_is_valid: provider (%s) JSON metadata did not contain a \"token_endpoint_auth_methods_supported\" array, assuming \"client_secret_basic\" is supported",
 
338
                oidc_debug(r,
 
339
                                "provider (%s) JSON metadata did not contain a \"token_endpoint_auth_methods_supported\" array, assuming \"client_secret_basic\" is supported",
327
340
                                issuer);
328
341
        } else {
329
342
                int i;
333
346
                        json_t *elem = json_array_get(
334
347
                                        j_token_endpoint_auth_methods_supported, i);
335
348
                        if (!json_is_string(elem)) {
336
 
                                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
337
 
                                                "oidc_metadata_provider_is_valid: unhandled in-array JSON object type [%d] in provider (%s) metadata for entry \"token_endpoint_auth_methods_supported\"",
 
349
                                oidc_warn(r,
 
350
                                                "unhandled in-array JSON object type [%d] in provider (%s) metadata for entry \"token_endpoint_auth_methods_supported\"",
338
351
                                                elem->type, issuer);
339
352
                                continue;
340
353
                        }
346
359
                        }
347
360
                }
348
361
                if (i == json_array_size(j_token_endpoint_auth_methods_supported)) {
349
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
350
 
                                        "oidc_metadata_provider_is_valid: could not find a supported value [client_secret_post|client_secret_basic] in provider (%s) metadata for entry \"token_endpoint_auth_methods_supported\"",
 
362
                        oidc_error(r,
 
363
                                        "could not find a supported value [client_secret_post|client_secret_basic] in provider (%s) metadata for entry \"token_endpoint_auth_methods_supported\"",
351
364
                                        issuer);
352
365
                        return FALSE;
353
366
                }
365
378
        /* get a handle to the client_id we need to use for this provider */
366
379
        json_t *j_client_id = json_object_get(j_client, "client_id");
367
380
        if ((j_client_id == NULL) || (!json_is_string(j_client_id))) {
368
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
369
 
                                "oidc_metadata_client_is_valid: client (%s) JSON metadata did not contain a \"client_id\" string",
 
381
                oidc_error(r,
 
382
                                "client (%s) JSON metadata did not contain a \"client_id\" string",
370
383
                                issuer);
371
384
                return FALSE;
372
385
        }
374
387
        /* get a handle to the client_secret we need to use for this provider */
375
388
        json_t *j_client_secret = json_object_get(j_client, "client_secret");
376
389
        if ((j_client_secret == NULL) || (!json_is_string(j_client_secret))) {
377
 
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
378
 
                                "oidc_metadata_client_is_valid: client (%s) JSON metadata did not contain a \"client_secret\" string",
 
390
                oidc_warn(r,
 
391
                                "client (%s) JSON metadata did not contain a \"client_secret\" string",
379
392
                                issuer);
380
393
                //return FALSE;
381
394
        }
383
396
        /* the expiry timestamp from the JSON object */
384
397
        json_t *expires_at = json_object_get(j_client, "client_secret_expires_at");
385
398
        if ((expires_at == NULL) || (!json_is_integer(expires_at))) {
386
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
387
 
                                "oidc_metadata_client_is_valid: client (%s) metadata did not contain a \"client_secret_expires_at\" setting",
 
399
                oidc_debug(r,
 
400
                                "client (%s) metadata did not contain a \"client_secret_expires_at\" setting",
388
401
                                issuer);
389
402
                /* assume that it never expires */
390
403
                return TRUE;
392
405
 
393
406
        /* see if it is unrestricted */
394
407
        if (json_integer_value(expires_at) == 0) {
395
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
396
 
                                "oidc_metadata_client_is_valid: client (%s) metadata never expires (client_secret_expires_at=0)",
 
408
                oidc_debug(r,
 
409
                                "client (%s) metadata never expires (client_secret_expires_at=0)",
397
410
                                issuer);
398
411
                return TRUE;
399
412
        }
400
413
 
401
414
        /* check if the value >= now */
402
415
        if (apr_time_sec(apr_time_now()) > json_integer_value(expires_at)) {
403
 
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
404
 
                                "oidc_metadata_client_is_valid: client (%s) secret expired",
405
 
                                issuer);
 
416
                oidc_warn(r, "client (%s) secret expired", issuer);
406
417
                return FALSE;
407
418
        }
408
419
 
409
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
410
 
                        "oidc_metadata_client_is_valid: client (%s) metadata is valid",
411
 
                        issuer);
 
420
        oidc_debug(r, "client (%s) metadata is valid", issuer);
412
421
 
413
422
        return TRUE;
414
423
}
421
430
 
422
431
        json_t *keys = json_object_get(j_jwks, "keys");
423
432
        if ((keys == NULL) || (!json_is_array(keys))) {
424
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
425
 
                                "oidc_metadata_jwks_is_valid: provider (%s) JWKS JSON metadata did not contain a \"keys\" array",
 
433
                oidc_error(r,
 
434
                                "provider (%s) JWKS JSON metadata did not contain a \"keys\" array",
426
435
                                issuer);
427
436
                return FALSE;
428
437
        }
429
438
        return TRUE;
430
439
}
431
440
 
 
441
/*
 
442
 * check is a specified JOSE feature is supported
 
443
 */
432
444
static apr_byte_t oidc_metadata_conf_jose_is_supported(request_rec *r,
433
445
                json_t *j_conf, const char *issuer, const char *key,
434
446
                apr_jose_is_supported_function_t jose_is_supported_function) {
435
447
        json_t *value = json_object_get(j_conf, key);
436
448
        if (value != NULL) {
437
449
                if (!json_is_string(value)) {
438
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
439
 
                                        "oidc_metadata_conf_jose_is_supported: (%s) JSON conf data has \"%s\" entry but it is not a string",
 
450
                        oidc_error(r,
 
451
                                        "(%s) JSON conf data has \"%s\" entry but it is not a string",
440
452
                                        issuer, key);
441
453
                        return FALSE;
442
454
                }
443
455
                if (jose_is_supported_function(r->pool,
444
456
                                json_string_value(value)) == FALSE) {
445
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
446
 
                                        "oidc_metadata_conf_jose_is_supported: (%s) JSON conf data has \"%s\" entry but it contains an unsupported algorithm or encryption type: \"%s\"",
 
457
                        oidc_error(r,
 
458
                                        "(%s) JSON conf data has \"%s\" entry but it contains an unsupported algorithm or encryption type: \"%s\"",
447
459
                                        issuer, key, json_string_value(value));
448
460
                        return FALSE;
449
461
                }
501
513
 
502
514
        /* try to open the metadata file for writing, creating it if it does not exist */
503
515
        if ((rc = apr_file_open(&fd, path, (APR_FOPEN_WRITE | APR_FOPEN_CREATE),
504
 
        APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
505
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
506
 
                                "oidc_metadata_file_write: file \"%s\" could not be opened (%s)",
507
 
                                path, apr_strerror(rc, s_err, sizeof(s_err)));
 
516
                        APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
 
517
                oidc_error(r, "file \"%s\" could not be opened (%s)", path,
 
518
                                apr_strerror(rc, s_err, sizeof(s_err)));
508
519
                return FALSE;
509
520
        }
510
521
 
521
532
 
522
533
        /* check for a system error */
523
534
        if (rc != APR_SUCCESS) {
524
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
525
 
                                "oidc_metadata_file_write: could not write to: \"%s\" (%s)",
526
 
                                path, apr_strerror(rc, s_err, sizeof(s_err)));
 
535
                oidc_error(r, "could not write to: \"%s\" (%s)", path,
 
536
                                apr_strerror(rc, s_err, sizeof(s_err)));
527
537
                return FALSE;
528
538
        }
529
539
 
530
540
        /* check that all bytes from the header were written */
531
541
        if (bytes_written != len) {
532
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
533
 
                                "oidc_metadata_file_write: could not write enough bytes to: \"%s\", bytes_written (%" APR_SIZE_T_FMT ") != len (%" APR_SIZE_T_FMT ")",
 
542
                oidc_error(r,
 
543
                                "could not write enough bytes to: \"%s\", bytes_written (%" APR_SIZE_T_FMT ") != len (%" APR_SIZE_T_FMT ")",
534
544
                                path, bytes_written, len);
535
545
                return FALSE;
536
546
        }
539
549
        apr_file_unlock(fd);
540
550
        apr_file_close(fd);
541
551
 
542
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
543
 
                        "oidc_metadata_file_write: file \"%s\" written; number of bytes (%" APR_SIZE_T_FMT ")",
 
552
        oidc_debug(r, "file \"%s\" written; number of bytes (%" APR_SIZE_T_FMT ")",
544
553
                        path, len);
545
554
 
546
555
        return TRUE;
547
556
}
548
557
 
549
 
/* callback function type for checking metadata validity (provider or client) */
550
 
typedef apr_byte_t (*oidc_is_valid_function_t)(request_rec *, json_t *,
551
 
                const char *);
552
 
 
553
 
/*
554
 
 * helper function to get the JSON (client or provider) metadata from the specified file path and check its validity
555
 
 */
556
 
static apr_byte_t oidc_metadata_get_and_check(request_rec *r, const char *path,
557
 
                const char *issuer, oidc_is_valid_function_t metadata_is_valid,
558
 
                json_t **j_metadata, apr_byte_t remove_when_invalid) {
559
 
 
560
 
        apr_finfo_t fi;
561
 
        apr_status_t rc = APR_SUCCESS;
562
 
        char s_err[128];
563
 
 
564
 
        /* read the metadata from a file in to a variable */
565
 
        if (oidc_metadata_file_read_json(r, path, j_metadata) == FALSE)
566
 
                goto error_delete;
567
 
 
568
 
        if (metadata_is_valid) {
569
 
                /* we've got metadata that is JSON and no error-JSON, but now we check provider/client validity */
570
 
                if (metadata_is_valid(r, *j_metadata, issuer) == FALSE)
571
 
                        goto error_delete;
572
 
        }
573
 
 
574
 
        /* all OK if we got here */
575
 
        return TRUE;
576
 
 
577
 
error_delete:
578
 
 
579
 
        /*
580
 
         * this is expired or otherwise invalid metadata, we're probably going to get
581
 
         * new metadata, so delete the file first, if it (still) exists at all
582
 
         */
583
 
        if ((remove_when_invalid == TRUE)
584
 
                        && (apr_stat(&fi, path, APR_FINFO_MTIME, r->pool) == APR_SUCCESS)) {
585
 
 
586
 
                if ((rc = apr_file_remove(path, r->pool)) != APR_SUCCESS) {
587
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
588
 
                                        "oidc_metadata_get_and_check: could not delete invalid metadata file %s (%s)",
589
 
                                        path, apr_strerror(rc, s_err, sizeof(s_err)));
590
 
                } else {
591
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
592
 
                                        "oidc_metadata_get_and_check: removed invalid metadata file %s",
593
 
                                        path);
594
 
                }
595
 
        }
596
 
 
597
 
        return FALSE;
598
 
}
599
 
 
600
 
/*
601
 
 * helper function to retrieve provider metadata from a URL, check it and store it
602
 
 */
603
 
static apr_byte_t oidc_metadata_provider_retrieve_and_store(request_rec *r,
604
 
                oidc_cfg *cfg, const char *url, const char *issuer, const char *path,
605
 
                json_t **j_metadata) {
606
 
        const char *response = NULL;
607
 
 
608
 
        /* no valid provider metadata, get it at the specified URL with the specified parameters */
609
 
        if (oidc_util_http_get(r, url, NULL, NULL, NULL,
610
 
                        cfg->provider.ssl_validate_server, &response,
611
 
                        cfg->http_timeout_short, cfg->outgoing_proxy) == FALSE)
612
 
                return FALSE;
613
 
 
614
 
        /* decode and see if it is not an error response somehow */
615
 
        if (oidc_util_decode_json_and_check_error(r, response, j_metadata) == FALSE)
616
 
                return FALSE;
617
 
 
618
 
        /* check to see if it is valid metadata */
619
 
        if (oidc_metadata_provider_is_valid(r, *j_metadata, issuer) == FALSE)
620
 
                return FALSE;
621
 
 
622
 
        /* since it is valid, write the obtained provider metadata file */
623
 
        if (oidc_metadata_file_write(r, path, response) == FALSE)
624
 
                return FALSE;
625
 
 
626
 
        /* all OK */
627
 
        return TRUE;
628
 
}
629
 
 
630
 
/*
631
 
 * helper function to retrieve client metadata from a dynamic registration URL, check it and store it
632
 
 */
633
 
static apr_byte_t oidc_metadata_client_retrieve_and_store(request_rec *r,
634
 
                oidc_cfg *cfg, const char *url, json_t *data, const char *issuer,
635
 
                const char *path, json_t **j_metadata, int ssl_validate_server,
636
 
                const char *bearer_token) {
637
 
        const char *response = NULL;
638
 
        apr_byte_t rc = FALSE;
639
 
 
640
 
        /*
641
 
        if (strstr(url,
642
 
                        "idp/client-registration.openid") != NULL) {
643
 
 
644
 
                apr_table_t *params = apr_table_make(r->pool, 3);
645
 
                json_t *v = json_object_get(data, "client_name");
646
 
                apr_table_addn(params, "client_name", json_string_value(v));
647
 
                apr_table_addn(params, "operation", "client_register");
648
 
                apr_table_addn(params, "redirect_uris", cfg->redirect_uri);
649
 
                rc = oidc_util_http_get(r, url, params, NULL, bearer_token,
650
 
                                ssl_validate_server, &response, cfg->http_timeout_short,
651
 
                                cfg->outgoing_proxy);
652
 
 
653
 
        } else {
654
 
        */
655
 
 
656
 
        /* no valid provider metadata, get it at the specified URL with the specified parameters */
657
 
        rc = oidc_util_http_post_json(r, url, data, NULL, bearer_token,
658
 
                        ssl_validate_server, &response, cfg->http_timeout_short,
659
 
                        cfg->outgoing_proxy);
660
 
 
 
558
/*
 
559
 * register the client with the OP using Dynamic Client Registration
 
560
 */
 
561
static apr_byte_t oidc_metadata_client_register(request_rec *r, oidc_cfg *cfg,
 
562
                oidc_provider_t *provider, json_t **j_client, const char **response) {
 
563
 
 
564
        /* assemble the JSON registration request */
 
565
        json_t *data = json_object();
 
566
        json_object_set_new(data, "client_name",
 
567
                        json_string(provider->client_name));
 
568
        json_object_set_new(data, "redirect_uris",
 
569
                        json_pack("[s]", cfg->redirect_uri));
 
570
 
 
571
        json_t *response_types = json_array();
 
572
        apr_array_header_t *flows = oidc_proto_supported_flows(r->pool);
 
573
        int i;
 
574
        for (i = 0; i < flows->nelts; i++) {
 
575
                json_array_append_new(response_types,
 
576
                                json_string(((const char**) flows->elts)[i]));
 
577
        }
 
578
        json_object_set_new(data, "response_types", response_types);
 
579
 
 
580
        if (provider->client_contact != NULL) {
 
581
                json_object_set_new(data, "contacts",
 
582
                                json_pack("[s]", provider->client_contact));
 
583
        }
 
584
 
 
585
        if (provider->client_jwks_uri) {
 
586
                json_object_set_new(data, "jwks_uri",
 
587
                                json_string(provider->client_jwks_uri));
 
588
        } else if (cfg->public_keys != NULL) {
 
589
                json_object_set_new(data, "jwks_uri",
 
590
                                json_string(
 
591
                                                apr_psprintf(r->pool, "%s?jwks=rsa",
 
592
                                                                cfg->redirect_uri)));
 
593
        }
 
594
 
 
595
        if (provider->id_token_signed_response_alg != NULL) {
 
596
                json_object_set_new(data, "id_token_signed_response_alg",
 
597
                                json_string(provider->id_token_signed_response_alg));
 
598
        }
 
599
        if (provider->id_token_encrypted_response_alg != NULL) {
 
600
                json_object_set_new(data, "id_token_encrypted_response_alg",
 
601
                                json_string(provider->id_token_encrypted_response_alg));
 
602
        }
 
603
        if (provider->id_token_encrypted_response_enc != NULL) {
 
604
                json_object_set_new(data, "id_token_encrypted_response_enc",
 
605
                                json_string(provider->id_token_encrypted_response_enc));
 
606
        }
 
607
 
 
608
        if (provider->userinfo_signed_response_alg != NULL) {
 
609
                json_object_set_new(data, "userinfo_signed_response_alg",
 
610
                                json_string(provider->userinfo_signed_response_alg));
 
611
        }
 
612
        if (provider->userinfo_encrypted_response_alg != NULL) {
 
613
                json_object_set_new(data, "userinfo_encrypted_response_alg",
 
614
                                json_string(provider->userinfo_encrypted_response_alg));
 
615
        }
 
616
        if (provider->userinfo_encrypted_response_enc != NULL) {
 
617
                json_object_set_new(data, "userinfo_encrypted_response_enc",
 
618
                                json_string(provider->userinfo_encrypted_response_enc));
 
619
        }
 
620
 
 
621
        json_object_set_new(data, "initiate_login_uri",
 
622
                        json_string(cfg->redirect_uri));
 
623
 
 
624
        /* dynamically register the client with the specified parameters */
 
625
        if (oidc_util_http_post_json(r, provider->registration_endpoint_url, data,
 
626
                        NULL, provider->registration_token, provider->ssl_validate_server, response,
 
627
                        cfg->http_timeout_short, cfg->outgoing_proxy) == FALSE) {
 
628
                json_decref(data);
 
629
                return FALSE;
 
630
        }
661
631
        json_decref(data);
662
 
        if (rc == FALSE)
663
 
                return FALSE;
664
632
 
665
633
        /* decode and see if it is not an error response somehow */
666
 
        if (oidc_util_decode_json_and_check_error(r, response, j_metadata) == FALSE)
667
 
                return FALSE;
668
 
 
669
 
        /* check to see if it is valid metadata */
670
 
        if (oidc_metadata_client_is_valid(r, *j_metadata, issuer) == FALSE)
671
 
                return FALSE;
672
 
 
673
 
        /* since it is valid, write the obtained provider metadata file */
674
 
        if (oidc_metadata_file_write(r, path, response) == FALSE)
675
 
                return FALSE;
676
 
 
677
 
        /* all OK */
678
 
        return TRUE;
 
634
        return oidc_util_decode_json_and_check_error(r, *response, j_client);
679
635
}
680
636
 
681
637
/*
682
638
 * helper function to get the JWKs for the specified issuer
683
639
 */
684
 
static apr_byte_t oidc_metadata_jwks_retrieve_and_store(request_rec *r,
 
640
static apr_byte_t oidc_metadata_jwks_retrieve_and_cache(request_rec *r,
685
641
                oidc_cfg *cfg, oidc_provider_t *provider, json_t **j_jwks) {
686
642
 
687
643
        const char *response = NULL;
701
657
                return FALSE;
702
658
 
703
659
        /* store the JWKs in the cache */
704
 
        cfg->cache->set(r, oidc_metadata_jwks_cache_key(r, provider->issuer),
705
 
                        response,
 
660
        cfg->cache->set(r, OIDC_CACHE_SECTION_JWKS,
 
661
                        oidc_metadata_jwks_cache_key(r, provider->issuer), response,
706
662
                        apr_time_now() + apr_time_from_sec(provider->jwks_refresh_interval));
707
663
 
708
664
        return TRUE;
714
670
apr_byte_t oidc_metadata_jwks_get(request_rec *r, oidc_cfg *cfg,
715
671
                oidc_provider_t *provider, json_t **j_jwks, apr_byte_t *refresh) {
716
672
 
717
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
718
 
                        "oidc_metadata_jwks_get: entering (issuer=%s, refresh=%d)",
719
 
                        provider->issuer, *refresh);
 
673
        oidc_debug(r, "enter, issuer=%s, refresh=%d", provider->issuer, *refresh);
720
674
 
721
675
        /* see if we need to do a forced refresh */
722
676
        if (*refresh == TRUE) {
723
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
724
 
                                "oidc_metadata_jwks_get: doing a forced refresh of the JWKs for issuer \"%s\"",
 
677
                oidc_debug(r, "doing a forced refresh of the JWKs for issuer \"%s\"",
725
678
                                provider->issuer);
726
 
                if (oidc_metadata_jwks_retrieve_and_store(r, cfg, provider,
 
679
                if (oidc_metadata_jwks_retrieve_and_cache(r, cfg, provider,
727
680
                                j_jwks) == TRUE)
728
681
                        return TRUE;
729
682
                // else: fallback on any cached JWKs
731
684
 
732
685
        /* see if the JWKs is cached */
733
686
        const char *value = NULL;
734
 
        cfg->cache->get(r, oidc_metadata_jwks_cache_key(r, provider->issuer),
735
 
                        &value);
 
687
        cfg->cache->get(r, OIDC_CACHE_SECTION_JWKS,
 
688
                        oidc_metadata_jwks_cache_key(r, provider->issuer), &value);
736
689
 
737
690
        if (value == NULL) {
738
691
                /* it is non-existing or expired: do a forced refresh */
739
692
                *refresh = TRUE;
740
 
                return oidc_metadata_jwks_retrieve_and_store(r, cfg, provider, j_jwks);
 
693
                return oidc_metadata_jwks_retrieve_and_cache(r, cfg, provider, j_jwks);
741
694
        }
742
695
 
743
696
        /* decode and see if it is not an error response somehow */
748
701
}
749
702
 
750
703
/*
 
704
 * use OpenID Connect Discovery to get metadata for the specified issuer
 
705
 */
 
706
apr_byte_t oidc_metadata_provider_retrieve(request_rec *r, oidc_cfg *cfg,
 
707
                const char *issuer, const char *url, json_t **j_metadata,
 
708
                const char **response) {
 
709
 
 
710
        /* get provider metadata from the specified URL with the specified parameters */
 
711
        if (oidc_util_http_get(r, url, NULL, NULL, NULL,
 
712
                        cfg->provider.ssl_validate_server, response,
 
713
                        cfg->http_timeout_short, cfg->outgoing_proxy) == FALSE)
 
714
                return FALSE;
 
715
 
 
716
        /* decode and see if it is not an error response somehow */
 
717
        if (oidc_util_decode_json_and_check_error(r, *response, j_metadata) == FALSE)
 
718
                return FALSE;
 
719
 
 
720
        /* check to see if it is valid metadata */
 
721
        if (oidc_metadata_provider_is_valid(r, *j_metadata, issuer) == FALSE)
 
722
                return FALSE;
 
723
 
 
724
        /* all OK */
 
725
        return TRUE;
 
726
}
 
727
 
 
728
/*
751
729
 * see if we have provider metadata and check its validity
752
 
 * if not, use OpenID Connect Provider Issuer Discovery to get it, check it and store it
 
730
 * if not, use OpenID Connect Discovery to get it, check it and store it
753
731
 */
754
732
static apr_byte_t oidc_metadata_provider_get(request_rec *r, oidc_cfg *cfg,
755
733
                const char *issuer, json_t **j_provider) {
756
734
 
 
735
        /* holds the response data/string/JSON from the OP */
 
736
        const char *response = NULL;
 
737
 
757
738
        /* get the full file path to the provider metadata for this issuer */
758
739
        const char *provider_path = oidc_metadata_provider_file_path(r, issuer);
759
740
 
760
741
        /* see if we have valid metadata already, if so, return it */
761
 
        if (oidc_metadata_get_and_check(r, provider_path, issuer,
762
 
                        oidc_metadata_provider_is_valid, j_provider, FALSE) == TRUE)
763
 
                return TRUE;
 
742
        if (oidc_metadata_file_read_json(r, provider_path, j_provider) == TRUE) {
 
743
 
 
744
                /* return the validation result */
 
745
                return oidc_metadata_provider_is_valid(r, *j_provider, issuer);
 
746
        }
764
747
 
765
748
        // TODO: how to do validity/expiry checks on provider metadata
766
749
 
768
751
        const char *url = apr_psprintf(r->pool, "%s",
769
752
                        ((strstr(issuer, "http://") == issuer)
770
753
                                        || (strstr(issuer, "https://") == issuer)) ?
771
 
                                        issuer : apr_psprintf(r->pool, "https://%s", issuer));
 
754
                                                        issuer : apr_psprintf(r->pool, "https://%s", issuer));
772
755
        url = apr_psprintf(r->pool, "%s%s.well-known/openid-configuration", url,
773
756
                        url[strlen(url) - 1] != '/' ? "/" : "");
774
757
 
775
 
        /* try and get it from there, checking it and storing it if successful */
776
 
        return oidc_metadata_provider_retrieve_and_store(r, cfg, url, issuer,
777
 
                        provider_path, j_provider);
 
758
        /* get the metadata for the issuer using OpenID Connect Discovery and validate it */
 
759
        if (oidc_metadata_provider_retrieve(r, cfg, issuer, url, j_provider,
 
760
                        &response) == FALSE)
 
761
                return FALSE;
 
762
 
 
763
        /* since it is valid, write the obtained provider metadata file */
 
764
        if (oidc_metadata_file_write(r, provider_path, response) == FALSE)
 
765
                return FALSE;
 
766
 
 
767
        return TRUE;
778
768
}
779
769
 
780
770
/*
791
781
        if (apr_stat(&fi, conf_path, APR_FINFO_MTIME, r->pool) != APR_SUCCESS)
792
782
                return TRUE;
793
783
 
794
 
        /* if it exists, parse and validate the conf metadata */
795
 
        return oidc_metadata_get_and_check(r, conf_path, issuer,
796
 
                        oidc_metadata_conf_is_valid, j_conf, FALSE);
 
784
        /* see if we have valid metadata already, if so, return it */
 
785
        if (oidc_metadata_file_read_json(r, conf_path, j_conf) == TRUE) {
 
786
 
 
787
                /* return the validation result */
 
788
                return oidc_metadata_conf_is_valid(r, *j_conf, issuer);
 
789
        }
 
790
 
 
791
        return FALSE;
797
792
}
798
793
 
799
794
/*
803
798
static apr_byte_t oidc_metadata_client_get(request_rec *r, oidc_cfg *cfg,
804
799
                const char *issuer, oidc_provider_t *provider, json_t **j_client) {
805
800
 
806
 
        /* get the full file path to the provider metadata for this issuer */
 
801
        /* get the full file path to the client metadata for this issuer */
807
802
        const char *client_path = oidc_metadata_client_file_path(r, issuer);
808
803
 
809
 
        /* see if we already have valid client metadata, if so, return TRUE */
810
 
        if (oidc_metadata_get_and_check(r, client_path, issuer,
811
 
                        oidc_metadata_client_is_valid, j_client, TRUE) == TRUE)
812
 
                return TRUE;
 
804
        /* see if we have valid metadata already, if so, return it */
 
805
        if (oidc_metadata_file_read_json(r, client_path, j_client) == TRUE) {
 
806
 
 
807
                /* if the client metadata is (still) valid, return it */
 
808
                if (oidc_metadata_client_is_valid(r, *j_client, issuer) == TRUE)
 
809
                        return TRUE;
 
810
        }
813
811
 
814
812
        /* at this point we have no valid client metadata, see if there's a registration endpoint for this provider */
815
813
        if (provider->registration_endpoint_url == NULL) {
816
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
817
 
                                "oidc_metadata_client_get: no (valid) client metadata exists for provider (%s) and provider JSON object did not contain a (valid) \"registration_endpoint\" string",
 
814
                oidc_error(r,
 
815
                                "no (valid) client metadata exists for provider (%s) and provider JSON object did not contain a (valid) \"registration_endpoint\" string",
818
816
                                issuer);
819
817
                return FALSE;
820
818
        }
821
819
 
822
 
        /* go and use Dynamic Client registration to fetch ourselves new client metadata */
823
 
        json_t *data = json_object();
824
 
        json_object_set_new(data, "client_name",
825
 
                        json_string(provider->client_name));
826
 
        json_object_set_new(data, "redirect_uris",
827
 
                        json_pack("[s]", cfg->redirect_uri));
828
 
 
829
 
        json_t *response_types = json_array();
830
 
        apr_array_header_t *flows = oidc_proto_supported_flows(r->pool);
831
 
        int i;
832
 
        for (i = 0; i < flows->nelts; i++) {
833
 
                json_array_append_new(response_types,
834
 
                                json_string(((const char**) flows->elts)[i]));
835
 
        }
836
 
        json_object_set_new(data, "response_types", response_types);
837
 
 
838
 
        if (provider->client_contact != NULL) {
839
 
                json_object_set_new(data, "contacts",
840
 
                                json_pack("[s]", provider->client_contact));
841
 
        }
842
 
 
843
 
        if (provider->client_jwks_uri) {
844
 
                json_object_set_new(data, "jwks_uri",
845
 
                                json_string(provider->client_jwks_uri));
846
 
        } else if (cfg->public_keys != NULL) {
847
 
                json_object_set_new(data, "jwks_uri",
848
 
                                json_string(
849
 
                                                apr_psprintf(r->pool, "%s?jwks=rsa",
850
 
                                                                cfg->redirect_uri)));
851
 
        }
852
 
 
853
 
        if (provider->id_token_signed_response_alg != NULL) {
854
 
                json_object_set_new(data, "id_token_signed_response_alg",
855
 
                                json_string(provider->id_token_signed_response_alg));
856
 
        }
857
 
        if (provider->id_token_encrypted_response_alg != NULL) {
858
 
                json_object_set_new(data, "id_token_encrypted_response_alg",
859
 
                                json_string(provider->id_token_encrypted_response_alg));
860
 
        }
861
 
        if (provider->id_token_encrypted_response_enc != NULL) {
862
 
                json_object_set_new(data, "id_token_encrypted_response_enc",
863
 
                                json_string(provider->id_token_encrypted_response_enc));
864
 
        }
865
 
 
866
 
        if (provider->userinfo_signed_response_alg != NULL) {
867
 
                json_object_set_new(data, "userinfo_signed_response_alg",
868
 
                                json_string(provider->userinfo_signed_response_alg));
869
 
        }
870
 
        if (provider->userinfo_encrypted_response_alg != NULL) {
871
 
                json_object_set_new(data, "userinfo_encrypted_response_alg",
872
 
                                json_string(provider->userinfo_encrypted_response_alg));
873
 
        }
874
 
        if (provider->userinfo_encrypted_response_enc != NULL) {
875
 
                json_object_set_new(data, "userinfo_encrypted_response_enc",
876
 
                                json_string(provider->userinfo_encrypted_response_enc));
877
 
        }
878
 
 
879
 
        json_object_set_new(data, "initiate_login_uri",
880
 
                        json_string(cfg->redirect_uri));
881
 
 
882
 
        /* try and get it from there, checking it and storing it if successful */
883
 
        return oidc_metadata_client_retrieve_and_store(r, cfg,
884
 
                        provider->registration_endpoint_url, data, issuer, client_path,
885
 
                        j_client, provider->ssl_validate_server,
886
 
                        provider->registration_token);
 
820
        /* try and get client metadata by registering the client at the registration endpoint */
 
821
        const char *response = NULL;
 
822
        if (oidc_metadata_client_register(r, cfg, provider, j_client,
 
823
                        &response) == FALSE)
 
824
                return FALSE;
 
825
 
 
826
        /* check to see if it is valid metadata */
 
827
        if (oidc_metadata_client_is_valid(r, *j_client, issuer) == FALSE)
 
828
                return FALSE;
 
829
 
 
830
        /* since it is valid, write the obtained client metadata file */
 
831
        if (oidc_metadata_file_write(r, client_path, response) == FALSE)
 
832
                return FALSE;
 
833
 
 
834
        return TRUE;
887
835
}
888
836
 
889
837
/*
896
844
        apr_finfo_t fi;
897
845
        char s_err[128];
898
846
 
899
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_metadata_list: entering");
 
847
        oidc_debug(r, "enter");
900
848
 
901
849
        /* open the metadata directory */
902
850
        if ((rc = apr_dir_open(&dir, cfg->metadata_dir, r->pool)) != APR_SUCCESS) {
903
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
904
 
                                "oidc_metadata_list: error opening metadata directory '%s' (%s)",
 
851
                oidc_error(r, "error opening metadata directory '%s' (%s)",
905
852
                                cfg->metadata_dir, apr_strerror(rc, s_err, sizeof(s_err)));
906
853
                return FALSE;
907
854
        }
927
874
 
928
875
                /* get the provider and client metadata, do all checks and registration if possible */
929
876
                oidc_provider_t *provider = NULL;
930
 
                if (oidc_metadata_get(r, cfg, issuer, &provider) == FALSE)
931
 
                        continue;
932
 
 
933
 
                /* push the decoded issuer filename in to the array */
934
 
                *(const char**) apr_array_push(*list) = issuer;
 
877
                if (oidc_metadata_get(r, cfg, issuer, &provider) == TRUE) {
 
878
                        /* push the decoded issuer filename in to the array */
 
879
                        *(const char**) apr_array_push(*list) = provider->issuer;
 
880
                }
935
881
        }
936
882
 
937
883
        /* we're done, cleanup now */
941
887
}
942
888
 
943
889
/*
944
 
 * find out what type of authentication we must provide to the token endpoint (we only support post or basic)
 
890
 * parse the JSON provider metadata in to a oidc_provider_t struct but do not override values already set
945
891
 */
946
 
static const char * oidc_metadata_token_endpoint_auth(request_rec *r,
947
 
                json_t *j_client, json_t *j_provider) {
948
 
 
949
 
        const char *result = "client_secret_basic";
950
 
 
951
 
        /* see if one is defined in the client metadata */
952
 
        json_t *token_endpoint_auth_method = json_object_get(j_client,
953
 
                        "token_endpoint_auth_method");
954
 
        if (token_endpoint_auth_method != NULL) {
955
 
                if (json_is_string(token_endpoint_auth_method)) {
956
 
                        if (strcmp(json_string_value(token_endpoint_auth_method),
957
 
                                        "client_secret_post") == 0) {
958
 
                                result = "client_secret_post";
959
 
                                return result;
960
 
                        }
961
 
                        if (strcmp(json_string_value(token_endpoint_auth_method),
962
 
                                        "client_secret_basic") == 0) {
963
 
                                result = "client_secret_basic";
964
 
                                return result;
965
 
                        }
966
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
967
 
                                        "oidc_metadata_token_endpoint_auth: unsupported client auth method \"%s\" in client metadata for entry \"token_endpoint_auth_method\"",
968
 
                                        json_string_value(token_endpoint_auth_method));
969
 
                } else {
970
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
971
 
                                        "oidc_metadata_token_endpoint_auth: unexpected JSON object type [%d] (!= APR_JSON_STRING) in client metadata for entry \"token_endpoint_auth_method\"",
972
 
                                        token_endpoint_auth_method->type);
973
 
                }
974
 
        }
975
 
 
976
 
        /* no supported value in the client metadata, find a supported one in the provider metadata */
977
 
        json_t *j_token_endpoint_auth_methods_supported = json_object_get(
978
 
                        j_provider, "token_endpoint_auth_methods_supported");
979
 
 
980
 
        if ((j_token_endpoint_auth_methods_supported != NULL)
981
 
                        && (json_is_array(j_token_endpoint_auth_methods_supported))) {
982
 
                int i;
983
 
                for (i = 0;
984
 
                                i < json_array_size(j_token_endpoint_auth_methods_supported);
985
 
                                i++) {
986
 
                        json_t *elem = json_array_get(
987
 
                                        j_token_endpoint_auth_methods_supported, i);
988
 
                        if (!json_is_string(elem)) {
989
 
                                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
990
 
                                                "oidc_metadata_token_endpoint_auth: unhandled in-array JSON object type [%d] in provider metadata for entry \"token_endpoint_auth_methods_supported\"",
991
 
                                                elem->type);
992
 
                                continue;
993
 
                        }
994
 
                        if (strcmp(json_string_value(elem), "client_secret_post") == 0) {
995
 
                                result = "client_secret_post";
996
 
                                break;
997
 
                        }
998
 
                        if (strcmp(json_string_value(elem), "client_secret_basic") == 0) {
999
 
                                result = "client_secret_basic";
1000
 
                                break;
1001
 
                        }
1002
 
                }
1003
 
        }
1004
 
 
1005
 
        return result;
 
892
apr_byte_t oidc_metadata_provider_parse(request_rec *r, json_t *j_provider,
 
893
                oidc_provider_t *provider) {
 
894
 
 
895
        if (provider->issuer == NULL) {
 
896
                /* get the "issuer" from the provider metadata */
 
897
                oidc_json_object_get_string(r->pool, j_provider, "issuer",
 
898
                                &provider->issuer, NULL);
 
899
        }
 
900
 
 
901
        if (provider->authorization_endpoint_url == NULL) {
 
902
                /* get a handle to the authorization endpoint */
 
903
                oidc_json_object_get_string(r->pool, j_provider,
 
904
                                "authorization_endpoint", &provider->authorization_endpoint_url,
 
905
                                NULL);
 
906
        }
 
907
 
 
908
        if (provider->token_endpoint_url == NULL) {
 
909
                /* get a handle to the token endpoint */
 
910
                oidc_json_object_get_string(r->pool, j_provider, "token_endpoint",
 
911
                                &provider->token_endpoint_url, NULL);
 
912
        }
 
913
 
 
914
        if (provider->userinfo_endpoint_url == NULL) {
 
915
                /* get a handle to the user_info endpoint */
 
916
                oidc_json_object_get_string(r->pool, j_provider, "userinfo_endpoint",
 
917
                                &provider->userinfo_endpoint_url, NULL);
 
918
        }
 
919
 
 
920
        if (provider->jwks_uri == NULL) {
 
921
                /* get a handle to the jwks_uri endpoint */
 
922
                oidc_json_object_get_string(r->pool, j_provider, "jwks_uri",
 
923
                                &provider->jwks_uri, NULL);
 
924
        }
 
925
 
 
926
        if (provider->registration_endpoint_url == NULL) {
 
927
                /* get a handle to the client registration endpoint */
 
928
                oidc_json_object_get_string(r->pool, j_provider,
 
929
                                "registration_endpoint", &provider->registration_endpoint_url,
 
930
                                NULL);
 
931
        }
 
932
 
 
933
        if (provider->check_session_iframe == NULL) {
 
934
                /* get a handle to the check session iframe */
 
935
                oidc_json_object_get_string(r->pool, j_provider, "check_session_iframe",
 
936
                                &provider->check_session_iframe, NULL);
 
937
        }
 
938
 
 
939
        if (provider->end_session_endpoint == NULL) {
 
940
                /* get a handle to the end session endpoint */
 
941
                oidc_json_object_get_string(r->pool, j_provider, "end_session_endpoint",
 
942
                                &provider->end_session_endpoint, NULL);
 
943
        }
 
944
 
 
945
        if (provider->token_endpoint_auth == NULL) {
 
946
 
 
947
                /* find a supported token_endpoint_auth_method in the provider metadata */
 
948
                json_t *j_token_endpoint_auth_methods_supported = json_object_get(
 
949
                                j_provider, "token_endpoint_auth_methods_supported");
 
950
 
 
951
                const char *token_endpoint_auth = NULL;
 
952
 
 
953
                /* loop through the array provided by the issuer and see if there's a supported method */
 
954
                if ((j_token_endpoint_auth_methods_supported != NULL)
 
955
                                && (json_is_array(j_token_endpoint_auth_methods_supported))) {
 
956
                        int i;
 
957
                        for (i = 0;
 
958
                                        i < json_array_size(j_token_endpoint_auth_methods_supported);
 
959
                                        i++) {
 
960
                                json_t *elem = json_array_get(
 
961
                                                j_token_endpoint_auth_methods_supported, i);
 
962
                                if (!json_is_string(elem)) {
 
963
                                        oidc_error(r,
 
964
                                                        "unhandled in-array JSON object type [%d] in provider metadata for entry \"token_endpoint_auth_methods_supported\"",
 
965
                                                        elem->type);
 
966
                                        continue;
 
967
                                }
 
968
 
 
969
                                /* take first supported method and prefer post over basic */
 
970
                                if ((apr_strnatcmp(json_string_value(elem),
 
971
                                                "client_secret_post") == 0)
 
972
                                                || (apr_strnatcmp(json_string_value(elem),
 
973
                                                                "client_secret_basic") == 0)) {
 
974
                                        token_endpoint_auth = json_string_value(elem);
 
975
                                        break;
 
976
                                }
 
977
                        }
 
978
                }
 
979
 
 
980
                /* store the method if found */
 
981
                if (token_endpoint_auth != NULL) {
 
982
                        provider->token_endpoint_auth = apr_pstrdup(r->pool,
 
983
                                        token_endpoint_auth);
 
984
                }
 
985
        }
 
986
 
 
987
        return TRUE;
1006
988
}
1007
989
 
1008
990
/*
1009
 
 * get the metadata for a specified issuer
1010
 
 *
1011
 
 * this fill the oidc_op_meta_t struct based on the issuer filename by reading and merging
1012
 
 * contents from both provider metadata directory and client metadata directory
 
991
 * parse the JSON conf metadata in to a oidc_provider_t struct
1013
992
 */
1014
 
apr_byte_t oidc_metadata_get(request_rec *r, oidc_cfg *cfg, const char *issuer,
1015
 
                oidc_provider_t **result) {
1016
 
 
1017
 
        /* pointer to the parsed JSON metadata for the provider */
1018
 
        json_t *j_provider = NULL;
1019
 
        /* pointer to the parsed JSON metadata for the client */
1020
 
        json_t *j_client = NULL;
1021
 
        /* pointer to the parsed conf metadata for the client */
1022
 
        json_t *j_conf = NULL;
1023
 
 
1024
 
        /* allocate space for a parsed-and-merged metadata struct */
1025
 
        *result = apr_pcalloc(r->pool, sizeof(oidc_provider_t));
1026
 
        /* convenient helper pointer */
1027
 
        oidc_provider_t *provider = *result;
1028
 
 
1029
 
        /* see if we can get valid provider metadata (possibly bootstrapping with Discovery), if not, return FALSE */
1030
 
        if (oidc_metadata_provider_get(r, cfg, issuer, &j_provider) == FALSE) {
1031
 
                if (j_provider)
1032
 
                        json_decref(j_provider);
1033
 
                return FALSE;
1034
 
        }
1035
 
 
1036
 
        /* get the "issuer" from the provider metadata */
1037
 
        oidc_json_object_get_string(r->pool, j_provider, "issuer",
1038
 
                        &provider->issuer, NULL);
1039
 
 
1040
 
        /* get a handle to the authorization endpoint */
1041
 
        oidc_json_object_get_string(r->pool, j_provider, "authorization_endpoint",
1042
 
                        &provider->authorization_endpoint_url, NULL);
1043
 
 
1044
 
        /* get a handle to the token endpoint */
1045
 
        oidc_json_object_get_string(r->pool, j_provider, "token_endpoint",
1046
 
                        &provider->token_endpoint_url, NULL);
1047
 
 
1048
 
        /* get a handle to the user_info endpoint */
1049
 
        oidc_json_object_get_string(r->pool, j_provider, "userinfo_endpoint",
1050
 
                        &provider->userinfo_endpoint_url, NULL);
1051
 
 
1052
 
        /* get a handle to the jwks_uri endpoint */
1053
 
        oidc_json_object_get_string(r->pool, j_provider, "jwks_uri",
1054
 
                        &provider->jwks_uri, NULL);
1055
 
 
1056
 
        /* get a handle to the client registration endpoint */
1057
 
        oidc_json_object_get_string(r->pool, j_provider, "registration_endpoint",
1058
 
                        &provider->registration_endpoint_url, NULL);
1059
 
 
1060
 
        /* see if we can get valid config metadata */
1061
 
        if (oidc_metadata_conf_get(r, cfg, issuer, &j_conf) == FALSE) {
1062
 
                if (j_provider)
1063
 
                        json_decref(j_provider);
1064
 
                if (j_conf)
1065
 
                        json_decref(j_conf);
1066
 
                return FALSE;
1067
 
        }
 
993
apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg *cfg,
 
994
                json_t *j_conf, oidc_provider_t *provider) {
1068
995
 
1069
996
        oidc_json_object_get_string(r->pool, j_conf, "client_jwks_uri",
1070
997
                        &provider->client_jwks_uri, cfg->provider.client_jwks_uri);
1117
1044
        oidc_json_object_get_string(r->pool, j_conf, "auth_request_params",
1118
1045
                        &provider->auth_request_params, cfg->provider.auth_request_params);
1119
1046
 
 
1047
        /* see if we've got custom token endpoint parameter values */
 
1048
        oidc_json_object_get_string(r->pool, j_conf, "token_endpoint_params",
 
1049
                        &provider->token_endpoint_params,
 
1050
                        cfg->provider.token_endpoint_params);
 
1051
 
1120
1052
        /* get the response mode to use */
1121
1053
        oidc_json_object_get_string(r->pool, j_conf, "response_mode",
1122
1054
                        &provider->response_mode, cfg->provider.response_mode);
1137
1069
        oidc_json_object_get_string(r->pool, j_conf, "response_type",
1138
1070
                        &provider->response_type, NULL);
1139
1071
 
1140
 
        if (oidc_metadata_client_get(r, cfg, issuer, provider, &j_client) == FALSE) {
1141
 
                if (j_provider)
1142
 
                        json_decref(j_provider);
1143
 
                if (j_conf)
1144
 
                        json_decref(j_conf);
1145
 
                if (j_client)
1146
 
                        json_decref(j_client);
1147
 
                return FALSE;
1148
 
        }
 
1072
        return TRUE;
 
1073
}
 
1074
 
 
1075
/*
 
1076
 * parse the JSON client metadata in to a oidc_provider_t struct
 
1077
 */
 
1078
apr_byte_t oidc_metadata_client_parse(request_rec *r, oidc_cfg *cfg,
 
1079
                json_t *j_client, oidc_provider_t *provider) {
1149
1080
 
1150
1081
        /* get a handle to the client_id we need to use for this provider */
1151
1082
        oidc_json_object_get_string(r->pool, j_client, "client_id",
1155
1086
        oidc_json_object_get_string(r->pool, j_client, "client_secret",
1156
1087
                        &provider->client_secret, NULL);
1157
1088
 
1158
 
        /* get the authentication method for the token endpoint */
1159
 
        provider->token_endpoint_auth = apr_pstrdup(r->pool,
1160
 
                        oidc_metadata_token_endpoint_auth(r, j_client, j_provider));
 
1089
        /* see if the token endpoint auth method defined in the client metadata overrides the provider one */
 
1090
        char *token_endpoint_auth = NULL;
 
1091
        oidc_json_object_get_string(r->pool, j_client, "token_endpoint_auth_method",
 
1092
                        &token_endpoint_auth, NULL);
 
1093
 
 
1094
        if (token_endpoint_auth != NULL) {
 
1095
                if ((apr_strnatcmp(token_endpoint_auth, "client_secret_post") == 0)
 
1096
                                || (apr_strnatcmp(token_endpoint_auth, "client_secret_basic")
 
1097
                                                == 0)) {
 
1098
                        provider->token_endpoint_auth = apr_pstrdup(r->pool,
 
1099
                                        token_endpoint_auth);
 
1100
                } else {
 
1101
                        oidc_warn(r,
 
1102
                                        "unsupported client auth method \"%s\" in client metadata for entry \"token_endpoint_auth_method\"",
 
1103
                                        token_endpoint_auth);
 
1104
                }
 
1105
        }
1161
1106
 
1162
1107
        /* determine the response type if not set by .conf */
1163
1108
        if (provider->response_type == NULL) {
1180
1125
                }
1181
1126
        }
1182
1127
 
 
1128
        return TRUE;
 
1129
}
 
1130
 
 
1131
/*
 
1132
 * get the metadata for a specified issuer
 
1133
 *
 
1134
 * this fill the oidc_provider_t struct based on the issuer filename by reading and merging
 
1135
 * contents from both provider metadata directory and client metadata directory
 
1136
 */
 
1137
apr_byte_t oidc_metadata_get(request_rec *r, oidc_cfg *cfg, const char *issuer,
 
1138
                oidc_provider_t **provider) {
 
1139
 
 
1140
        apr_byte_t rc = FALSE;
 
1141
 
 
1142
        /* pointers to the parsed JSON metadata */
 
1143
        json_t *j_provider = NULL;
 
1144
        json_t *j_client = NULL;
 
1145
        json_t *j_conf = NULL;
 
1146
 
 
1147
        /* allocate space for a parsed-and-merged metadata struct */
 
1148
        *provider = apr_pcalloc(r->pool, sizeof(oidc_provider_t));
 
1149
 
 
1150
        /*
 
1151
         * read and parse the provider, conf and client metadata respectively
 
1152
         * NB: order is important here
 
1153
         */
 
1154
 
 
1155
        if (oidc_metadata_provider_get(r, cfg, issuer, &j_provider) == FALSE)
 
1156
                goto end;
 
1157
        if (oidc_metadata_provider_parse(r, j_provider, *provider) == FALSE)
 
1158
                goto end;
 
1159
 
 
1160
        if (oidc_metadata_conf_get(r, cfg, issuer, &j_conf) == FALSE)
 
1161
                goto end;
 
1162
        if (oidc_metadata_conf_parse(r, cfg, j_conf, *provider) == FALSE)
 
1163
                goto end;
 
1164
 
 
1165
        if (oidc_metadata_client_get(r, cfg, issuer, *provider, &j_client) == FALSE)
 
1166
                goto end;
 
1167
        if (oidc_metadata_client_parse(r, cfg, j_client, *provider) == FALSE)
 
1168
                goto end;
 
1169
 
 
1170
        rc = TRUE;
 
1171
 
 
1172
end:
1183
1173
        if (j_provider)
1184
1174
                json_decref(j_provider);
1185
1175
        if (j_conf)
1187
1177
        if (j_client)
1188
1178
                json_decref(j_client);
1189
1179
 
1190
 
        return TRUE;
 
1180
        return rc;
1191
1181
}
1192
1182