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

« back to all changes in this revision

Viewing changes to src/mod_auth_openidc.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:
133
133
                if (!prefix_matches && !authn_header_matches) {
134
134
                        apr_table_addn(clean_headers, k, e[i].val);
135
135
                } else {
136
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
137
 
                                        "oidc_scrub_request_headers: scrubbed suspicious request header (%s: %.32s)",
138
 
                                        k, e[i].val);
 
136
                        oidc_warn(r, "scrubbed suspicious request header (%s: %.32s)", k,
 
137
                                        e[i].val);
139
138
                }
140
139
        }
141
140
 
148
147
 */
149
148
static char *oidc_get_browser_state_hash(request_rec *r, const char *nonce) {
150
149
 
151
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
152
 
                        "oidc_get_browser_state_hash: entering");
 
150
        oidc_debug(r, "enter");
153
151
 
154
152
        /* helper to hold to header values */
155
153
        const char *value = NULL;
187
185
        unsigned char hash[sha1_len];
188
186
        apr_sha1_final(hash, &sha1);
189
187
 
190
 
        /* base64 encode the resulting hash and return it */
191
 
        char *result = apr_palloc(r->pool, apr_base64_encode_len(sha1_len) + 1);
192
 
        apr_base64_encode(result, (const char *) hash, sha1_len);
 
188
        /* base64url-encode the resulting hash and return it */
 
189
        char *result = NULL;
 
190
        oidc_base64url_encode(r, &result, (const char *) hash, sha1_len, TRUE);
193
191
        return result;
194
192
}
195
193
 
196
194
/*
 
195
 * return the name for the state cookie
 
196
 */
 
197
static char *oidc_get_state_cookie_name(request_rec *r, const char *state) {
 
198
        return apr_psprintf(r->pool, "%s%s", OIDCStateCookiePrefix, state);
 
199
}
 
200
 
 
201
/*
 
202
 * return the static provider configuration, i.e. from a metadata URL or configuration primitives
 
203
 */
 
204
static apr_byte_t oidc_provider_static_config(request_rec *r, oidc_cfg *c, oidc_provider_t **provider) {
 
205
 
 
206
        json_t *j_provider = NULL;
 
207
        const char *s_json = NULL;
 
208
 
 
209
        /* see if we should configure a static provider based on external (cached) metadata */
 
210
        if ((c->metadata_dir != NULL) || (c->provider.metadata_url == NULL)) {
 
211
                *provider = &c->provider;
 
212
                return TRUE;
 
213
        }
 
214
 
 
215
        c->cache->get(r, OIDC_CACHE_SECTION_PROVIDER, c->provider.metadata_url,
 
216
                        &s_json);
 
217
 
 
218
        if (s_json == NULL) {
 
219
 
 
220
                if (oidc_metadata_provider_retrieve(r, c, NULL,
 
221
                                c->provider.metadata_url, &j_provider, &s_json) == FALSE) {
 
222
                        oidc_error(r, "could not retrieve metadata from url: %s",
 
223
                                        c->provider.metadata_url);
 
224
                        return FALSE;
 
225
                }
 
226
 
 
227
                // TODO: make the expiry configurable
 
228
                c->cache->set(r, OIDC_CACHE_SECTION_PROVIDER, c->provider.metadata_url,
 
229
                                s_json,
 
230
                                apr_time_now() + apr_time_from_sec(OIDC_CACHE_PROVIDER_METADATA_EXPIRY_DEFAULT));
 
231
 
 
232
        } else {
 
233
 
 
234
                /* correct parsing and validation was already done when it was put in the cache */
 
235
                j_provider = json_loads(s_json, 0, 0);
 
236
        }
 
237
 
 
238
        *provider = apr_pcalloc(r->pool, sizeof(oidc_provider_t));
 
239
        memcpy(*provider, &c->provider, sizeof(oidc_provider_t));
 
240
 
 
241
        if (oidc_metadata_provider_parse(r, j_provider, *provider) == FALSE) {
 
242
                oidc_error(r, "could not parse metadata from url: %s",
 
243
                                c->provider.metadata_url);
 
244
                if (j_provider)
 
245
                        json_decref(j_provider);
 
246
                return FALSE;
 
247
        }
 
248
 
 
249
        json_decref(j_provider);
 
250
 
 
251
        return TRUE;
 
252
}
 
253
 
 
254
/*
197
255
 * return the oidc_provider_t struct for the specified issuer
198
256
 */
199
 
static oidc_provider_t *oidc_get_provider_for_issuer(request_rec *r, oidc_cfg *c, const char *issuer) {
 
257
static oidc_provider_t *oidc_get_provider_for_issuer(request_rec *r,
 
258
                oidc_cfg *c, const char *issuer) {
200
259
 
201
260
        /* by default we'll assume that we're dealing with a single statically configured OP */
202
 
        oidc_provider_t *provider = &c->provider;
 
261
        oidc_provider_t *provider = NULL;
 
262
        if (oidc_provider_static_config(r, c, &provider) == FALSE)
 
263
                return NULL;
203
264
 
204
265
        /* unless a metadata directory was configured, so we'll try and get the provider settings from there */
205
266
        if (c->metadata_dir != NULL) {
206
267
 
207
268
                /* try and get metadata from the metadata directory for the OP that sent this response */
208
 
                if ((oidc_metadata_get(r, c, issuer, &provider)
209
 
                                == FALSE) || (provider == NULL)) {
 
269
                if ((oidc_metadata_get(r, c, issuer, &provider) == FALSE)
 
270
                                || (provider == NULL)) {
210
271
 
211
272
                        /* don't know nothing about this OP/issuer */
212
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
213
 
                                        "oidc_get_provider_for_issuer: no provider metadata found for issuer \"%s\"",
 
273
                        oidc_error(r, "no provider metadata found for issuer \"%s\"",
214
274
                                        issuer);
215
275
 
216
276
                        return NULL;
226
286
static apr_byte_t oidc_unsolicited_proto_state(request_rec *r, oidc_cfg *c,
227
287
                const char *state, oidc_proto_state **proto_state) {
228
288
 
229
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
230
 
                        "oidc_unsolicited_proto_state: entering");
 
289
        oidc_debug(r, "enter");
231
290
 
232
291
        apr_jwt_t *jwt = NULL;
233
292
        if (apr_jwt_parse(r->pool, state, &jwt, c->private_keys,
234
293
                        c->provider.client_secret) == FALSE) {
235
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
236
 
                                "oidc_unsolicited_proto_state: could not parse JWT from state: invalid unsolicited response");
 
294
                oidc_error(r,
 
295
                                "could not parse JWT from state: invalid unsolicited response");
237
296
                return FALSE;
238
297
        }
239
298
 
240
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
241
 
                        "oidc_unsolicited_proto_state: successfully parsed JWT from state");
 
299
        oidc_debug(r, "successfully parsed JWT from state");
242
300
 
243
301
        if (jwt->payload.iss == NULL) {
244
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
245
 
                                "oidc_unsolicited_proto_state: no \"iss\" could be retrieved from JWT state, aborting");
 
302
                oidc_error(r, "no \"iss\" could be retrieved from JWT state, aborting");
246
303
                apr_jwt_destroy(jwt);
247
304
                return FALSE;
248
305
        }
257
314
        char *rfp = NULL;
258
315
        apr_jwt_get_string(r->pool, &jwt->payload.value, "rfp", &rfp);
259
316
        if (rfp == NULL) {
260
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
261
 
                                "oidc_unsolicited_proto_state: no \"rfp\" claim could be retrieved from JWT state, aborting");
 
317
                oidc_error(r,
 
318
                                "no \"rfp\" claim could be retrieved from JWT state, aborting");
262
319
                apr_jwt_destroy(jwt);
263
320
                return FALSE;
264
321
        }
265
322
 
266
323
        if (strcmp(rfp, "iss") != 0) {
267
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
268
 
                                "oidc_unsolicited_proto_state: \"rfp\" (%s) does not match \"iss\", aborting",
269
 
                                rfp);
 
324
                oidc_error(r, "\"rfp\" (%s) does not match \"iss\", aborting", rfp);
270
325
                apr_jwt_destroy(jwt);
271
326
                return FALSE;
272
327
        }
273
328
 
274
329
        char *target_link_uri = NULL;
275
 
        apr_jwt_get_string(r->pool, &jwt->payload.value, "target_uri", &target_link_uri);
 
330
        apr_jwt_get_string(r->pool, &jwt->payload.value, "target_uri",
 
331
                        &target_link_uri);
276
332
        if (target_link_uri == NULL) {
277
 
                if (c->default_url == NULL) {
278
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
279
 
                                "oidc_unsolicited_proto_state: no \"target_uri\" claim could be retrieved from JWT state and no OIDCDefaultURL is set, aborting");
 
333
                if (c->default_sso_url == NULL) {
 
334
                        oidc_error(r,
 
335
                                        "no \"target_uri\" claim could be retrieved from JWT state and no OIDCDefaultURL is set, aborting");
280
336
                        apr_jwt_destroy(jwt);
281
337
                        return FALSE;
282
338
                }
283
 
                target_link_uri = c->default_url;
 
339
                target_link_uri = c->default_sso_url;
284
340
        }
285
341
 
286
342
        if (c->metadata_dir != NULL) {
287
343
                if ((oidc_metadata_get(r, c, jwt->payload.iss, &provider) == FALSE)
288
344
                                || (provider == NULL)) {
289
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
290
 
                                        "oidc_unsolicited_proto_state: no provider metadata found for provider \"%s\"",
 
345
                        oidc_error(r, "no provider metadata found for provider \"%s\"",
291
346
                                        jwt->payload.iss);
292
347
                        apr_jwt_destroy(jwt);
293
348
                        return FALSE;
295
350
        }
296
351
 
297
352
        if ((jwt->payload.exp != APR_JWT_CLAIM_TIME_EMPTY)
298
 
                        && (oidc_proto_validate_exp(r, jwt) == FALSE))
 
353
                        && (oidc_proto_validate_exp(r, jwt) == FALSE)) {
299
354
                apr_jwt_destroy(jwt);
300
355
                return FALSE;
 
356
        }
301
357
 
302
358
        if ((jwt->payload.iat != APR_JWT_CLAIM_TIME_EMPTY)
303
359
                        && (oidc_proto_validate_iat(r, provider, jwt) == FALSE)) {
313
369
        }
314
370
 
315
371
        const char *replay = NULL;
316
 
        c->cache->get(r, jti, &replay);
 
372
        c->cache->get(r, OIDC_CACHE_SECTION_JTI, jti, &replay);
317
373
        if (replay != NULL) {
318
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
319
 
                                "oidc_unsolicited_proto_state: the jti value (%s) passed in the browser state was found in the cache already; possible replay attack!?",
 
374
                oidc_error(r,
 
375
                                "the jti value (%s) passed in the browser state was found in the cache already; possible replay attack!?",
320
376
                                jti);
321
377
                apr_jwt_destroy(jwt);
322
378
                return FALSE;
327
383
                        provider->idtoken_iat_slack * 2 + 10);
328
384
 
329
385
        /* store it in the cache for the calculated duration */
330
 
        c->cache->set(r, jti, jti, apr_time_now() + jti_cache_duration);
 
386
        c->cache->set(r, OIDC_CACHE_SECTION_JTI, jti, jti,
 
387
                        apr_time_now() + jti_cache_duration);
331
388
 
332
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
333
 
                        "oidc_unsolicited_proto_state: jti \"%s\" validated successfully and is now cached for %" APR_TIME_T_FMT " seconds",
 
389
        oidc_debug(r,
 
390
                        "jti \"%s\" validated successfully and is now cached for %" APR_TIME_T_FMT " seconds",
334
391
                        jti, apr_time_sec(jti_cache_duration));
335
392
 
336
393
        /*
352
409
        //       (now we always use the statically configured provider client_secret...)
353
410
        // TODO: check c_hash unless implicit (no at_hash because oidc > oauth, right?)
354
411
        // TODO: move this code somehow to jose/ ?
355
 
 
356
412
        apr_byte_t refresh = FALSE;
357
413
        if (oidc_proto_idtoken_verify_signature(r, c, provider, jwt,
358
414
                        &refresh) == FALSE) {
359
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
360
 
                                "oidc_unsolicited_proto_state: state JWT signature could not be validated, aborting");
 
415
                oidc_error(r, "state JWT signature could not be validated, aborting");
361
416
                apr_jwt_destroy(jwt);
362
417
                return FALSE;
363
418
        }
364
419
 
365
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
366
 
                        "oidc_unsolicited_proto_state: successfully verified state JWT");
 
420
        oidc_debug(r, "successfully verified state JWT");
367
421
 
368
422
        *proto_state = apr_pcalloc(r->pool, sizeof(oidc_proto_state));
369
423
        oidc_proto_state *res = *proto_state;
375
429
        res->original_url = target_link_uri;
376
430
        res->response_mode = provider->response_mode;
377
431
        res->response_type = provider->response_type;
 
432
        res->prompt = NULL;
378
433
        res->timestamp = apr_time_sec(apr_time_now());
379
434
 
380
435
        apr_jwt_destroy(jwt);
388
443
static apr_byte_t oidc_restore_proto_state(request_rec *r, oidc_cfg *c,
389
444
                const char *state, oidc_proto_state **proto_state) {
390
445
 
391
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
392
 
                        "oidc_restore_proto_state: entering");
 
446
        oidc_debug(r, "enter");
 
447
 
 
448
        const char *cookieName = oidc_get_state_cookie_name(r, state);
393
449
 
394
450
        /* get the state cookie value first */
395
 
        char *cookieValue = oidc_util_get_cookie(r, OIDCStateCookieName);
 
451
        char *cookieValue = oidc_util_get_cookie(r, cookieName);
396
452
        if (cookieValue == NULL) {
397
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
398
 
                                "oidc_restore_proto_state: no \"%s\" state cookie found",
399
 
                                OIDCStateCookieName);
 
453
                oidc_error(r, "no \"%s\" state cookie found", cookieName);
400
454
                return oidc_unsolicited_proto_state(r, c, state, proto_state);
401
455
        }
402
456
 
403
457
        /* clear state cookie because we don't need it anymore */
404
 
        oidc_util_set_cookie(r, OIDCStateCookieName, "");
 
458
        oidc_util_set_cookie(r, cookieName, "", 0);
405
459
 
406
460
        /* decrypt the state obtained from the cookie */
407
461
        char *svalue = NULL;
408
462
        if (oidc_base64url_decode_decrypt_string(r, &svalue, cookieValue) <= 0)
409
463
                return FALSE;
410
464
 
411
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
412
 
                        "oidc_restore_proto_state: restored JSON state cookie value: %s",
413
 
                        svalue);
 
465
        oidc_debug(r, "restored JSON state cookie value: %s", svalue);
414
466
 
415
467
        *proto_state = apr_pcalloc(r->pool, sizeof(oidc_proto_state));
416
468
        oidc_proto_state *res = *proto_state;
418
470
        json_error_t json_error;
419
471
        json_t *v, *json = json_loads(svalue, 0, &json_error);
420
472
        if (json == NULL) {
421
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_restore_proto_state: parsing JSON (json_loads) failed: %s", json_error.text);
 
473
                oidc_error(r, "parsing JSON (json_loads) failed: %s", json_error.text);
422
474
                return FALSE;
423
475
        }
424
476
 
429
481
        char *calc = oidc_get_browser_state_hash(r, res->nonce);
430
482
        /* compare the calculated hash with the value provided in the authorization response */
431
483
        if (apr_strnatcmp(calc, state) != 0) {
432
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
433
 
                                "oidc_restore_proto_state: calculated state from cookie does not match state parameter passed back in URL: \"%s\" != \"%s\"",
 
484
                oidc_error(r,
 
485
                                "calculated state from cookie does not match state parameter passed back in URL: \"%s\" != \"%s\"",
434
486
                                state, calc);
435
487
                json_decref(json);
436
488
                return FALSE;
449
501
        res->response_type = apr_pstrdup(r->pool, json_string_value(v));
450
502
 
451
503
        v = json_object_get(json, "response_mode");
452
 
        res->response_mode = (strcmp(json_string_value(v), "") != 0) ? apr_pstrdup(r->pool, json_string_value(v)) : NULL;
 
504
        res->response_mode =
 
505
                        (strcmp(json_string_value(v), "") != 0) ?
 
506
                                        apr_pstrdup(r->pool, json_string_value(v)) : NULL;
 
507
 
 
508
        v = json_object_get(json, "prompt");
 
509
        res->prompt =
 
510
                        (strcmp(json_string_value(v), "") != 0) ?
 
511
                                        apr_pstrdup(r->pool, json_string_value(v)) : NULL;
453
512
 
454
513
        v = json_object_get(json, "timestamp");
455
514
        res->timestamp = json_integer_value(v);
457
516
        /* check that the timestamp is not beyond the valid interval */
458
517
        apr_time_t now = apr_time_sec(apr_time_now());
459
518
        if (now > res->timestamp + c->state_timeout) {
460
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
461
 
                                "oidc_restore_proto_state: state has expired");
 
519
                oidc_error(r, "state has expired");
462
520
                json_decref(json);
463
521
                return FALSE;
464
522
        }
465
523
 
466
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
467
 
                        "oidc_restore_proto_state: restored state: nonce=\"%s\", original_url=\"%s\", original_method=\"%s\", issuer=\"%s\", response_type=\%s\", response_mode=\"%s\", timestamp=%" APR_TIME_T_FMT,
468
 
                        res->nonce, res->original_url, res->original_method, res->issuer, res->response_type,
469
 
                        res->response_mode, res->timestamp);
 
524
        oidc_debug(r,
 
525
                        "restored state: nonce=\"%s\", original_url=\"%s\", original_method=\"%s\", issuer=\"%s\", response_type=\%s\", response_mode=\"%s\", timestamp=%" APR_TIME_T_FMT,
 
526
                        res->nonce, res->original_url, res->original_method, res->issuer,
 
527
                        res->response_type, res->response_mode, res->timestamp);
470
528
 
471
529
        json_decref(json);
472
530
 
479
537
 * in a cookie in the browser that is cryptographically bound to that state
480
538
 */
481
539
static apr_byte_t oidc_authorization_request_set_cookie(request_rec *r,
482
 
                oidc_proto_state *proto_state) {
 
540
                oidc_cfg *c, const char *state, oidc_proto_state *proto_state) {
483
541
        /*
484
 
         * create a cookie consisting of 5 elements:
485
 
         * random value, original URL, issuer, response_type and timestamp
 
542
         * create a cookie consisting of 8 elements:
 
543
         * random value, original URL, original method, issuer, response_type, response_mod, prompt and timestamp
486
544
         * encoded as JSON
487
545
         */
488
546
        char *plainText = apr_psprintf(r->pool, "{"
492
550
                        "\"issuer\": \"%s\","
493
551
                        "\"response_type\": \"%s\","
494
552
                        "\"response_mode\": \"%s\","
 
553
                        "\"prompt\": \"%s\","
495
554
                        "\"timestamp\": %" APR_TIME_T_FMT "}", proto_state->nonce,
496
 
                        proto_state->original_url, proto_state->original_method, proto_state->issuer,
497
 
                        proto_state->response_type,
 
555
                        proto_state->original_url, proto_state->original_method,
 
556
                        proto_state->issuer, proto_state->response_type,
498
557
                        proto_state->response_mode ? proto_state->response_mode : "",
 
558
                        proto_state->prompt ? proto_state->prompt : "",
499
559
                        proto_state->timestamp);
500
560
 
501
561
        /* encrypt the resulting JSON value  */
502
562
        char *cookieValue = NULL;
503
563
        if (oidc_encrypt_base64url_encode_string(r, &cookieValue, plainText) <= 0) {
504
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
505
 
                                "oidc_authorization_request_set_cookie: oidc_encrypt_base64url_encode_string failed");
 
564
                oidc_error(r, "oidc_encrypt_base64url_encode_string failed");
506
565
                return FALSE;
507
566
        }
508
567
 
 
568
        /* assemble the cookie name for the state cookie */
 
569
        const char *cookieName = oidc_get_state_cookie_name(r, state);
 
570
 
509
571
        /* set it as a cookie */
510
 
        oidc_util_set_cookie(r, OIDCStateCookieName, cookieValue);
 
572
        oidc_util_set_cookie(r, cookieName, cookieValue,
 
573
                        apr_time_now() + apr_time_from_sec(c->state_timeout));
511
574
 
512
575
        return TRUE;
513
576
}
582
645
 
583
646
                if (j_claims == NULL) {
584
647
                        /* whoops, JSON has been corrupted */
585
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
586
 
                                        "oidc_set_app_claims: unable to parse \"%s\" JSON stored in the session (%s), returning internal server error",
 
648
                        oidc_error(r,
 
649
                                        "unable to parse \"%s\" JSON stored in the session (%s), returning internal server error",
587
650
                                        json_error.text, session_key);
588
651
 
589
652
                        return FALSE;
611
674
static int oidc_handle_existing_session(request_rec *r,
612
675
                const oidc_cfg * const cfg, session_rec *session) {
613
676
 
614
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
615
 
                        "oidc_handle_existing_session: entering");
 
677
        oidc_debug(r, "enter");
616
678
 
617
679
        /* get a handle to the director config */
618
680
        oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
628
690
 
629
691
        /* set the user authentication HTTP header if set and required */
630
692
        if ((r->user != NULL) && (dir_cfg->authn_header != NULL)) {
631
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
632
 
                                "oidc_handle_existing_session: setting authn header (%s) to: %s", dir_cfg->authn_header, r->user);
 
693
                oidc_debug(r, "setting authn header (%s) to: %s", dir_cfg->authn_header,
 
694
                                r->user);
633
695
                apr_table_set(r->headers_in, dir_cfg->authn_header, r->user);
634
696
        }
635
697
 
639
701
 
640
702
        if ((cfg->pass_idtoken_as & OIDC_PASS_IDTOKEN_AS_CLAIMS)) {
641
703
                /* set the id_token in the app headers + request state */
642
 
                if (oidc_set_app_claims(r, cfg, session, OIDC_IDTOKEN_CLAIMS_SESSION_KEY) == FALSE)
 
704
                if (oidc_set_app_claims(r, cfg, session,
 
705
                                OIDC_IDTOKEN_CLAIMS_SESSION_KEY) == FALSE)
643
706
                        return HTTP_INTERNAL_SERVER_ERROR;
644
707
        }
645
708
 
646
709
        if ((cfg->pass_idtoken_as & OIDC_PASS_IDTOKEN_AS_PAYLOAD)) {
647
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
648
 
                                "oidc_handle_existing_session: setting OIDC_id_token_payload header");
 
710
                oidc_debug(r, "setting OIDC_id_token_payload header");
649
711
                const char *s_id_token = NULL;
650
712
                /* get the string-encoded JSON object from the session */
651
 
                oidc_session_get(r, session, OIDC_IDTOKEN_CLAIMS_SESSION_KEY, &s_id_token);
 
713
                oidc_session_get(r, session, OIDC_IDTOKEN_CLAIMS_SESSION_KEY,
 
714
                                &s_id_token);
652
715
                /* pass it to the app in a header */
653
716
                oidc_util_set_app_header(r, "id_token_payload", s_id_token, "OIDC_");
654
717
        }
655
718
 
656
719
        if ((cfg->pass_idtoken_as & OIDC_PASS_IDTOKEN_AS_SERIALIZED)) {
657
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
658
 
                                "oidc_handle_existing_session: setting OIDC_id_token header");
 
720
                oidc_debug(r, "setting OIDC_id_token header");
659
721
                const char *s_id_token = NULL;
660
722
                /* get the compact serialized JWT from the session */
661
723
                oidc_session_get(r, session, OIDC_IDTOKEN_SESSION_KEY, &s_id_token);
707
769
                oidc_cfg *c, const char *state, struct oidc_provider_t **provider,
708
770
                oidc_proto_state **proto_state) {
709
771
 
710
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
711
 
                        "oidc_authorization_response_match_state: entering (state=%s)",
712
 
                        state);
 
772
        oidc_debug(r, "enter (state=%s)", state);
713
773
 
714
774
        if ((state == NULL) || (apr_strnatcmp(state, "") == 0)) {
715
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
716
 
                                "oidc_authorization_response_match_state: state parameter is not set");
 
775
                oidc_error(r, "state parameter is not set");
717
776
                return FALSE;
718
777
        }
719
778
 
720
779
        /* check the state parameter against what we stored in a cookie */
721
780
        if (oidc_restore_proto_state(r, c, state, proto_state) == FALSE) {
722
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
723
 
                                "oidc_authorization_response_match_state: unable to restore state");
 
781
                oidc_error(r, "unable to restore state");
724
782
                return FALSE;
725
783
        }
726
784
 
727
 
        *provider = oidc_get_provider_for_issuer(r, c,  (*proto_state)->issuer);
 
785
        *provider = oidc_get_provider_for_issuer(r, c, (*proto_state)->issuer);
728
786
 
729
787
        return (*provider != NULL);
730
788
}
761
819
                                        "  </body>\n"
762
820
                                        "</html>\n";
763
821
        java_script = apr_psprintf(r->pool, java_script, original_url);
764
 
        return oidc_util_http_sendstring(r, java_script, DONE);
765
 
}
766
 
 
 
822
        return oidc_util_html_send(r, java_script, DONE);
 
823
}
 
824
 
 
825
/*
 
826
 * redirect the browser to the session logout endpoint
 
827
 */
 
828
static int oidc_session_redirect_parent_window_to_logout(request_rec *r,
 
829
                oidc_cfg *c) {
 
830
        oidc_debug(r, "enter");
 
831
        char *html =
 
832
                        apr_psprintf(r->pool,
 
833
                                        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
 
834
                                                        "<html>\n"
 
835
                                                        "  <head>\n"
 
836
                                                        "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
 
837
                                                        "    <script type=\"text/javascript\">\n"
 
838
                                                        "      window.top.location.href = '%s?session=logout';\n"
 
839
                                                        "    </script>\n"
 
840
                                                        "  </head>\n"
 
841
                                                        "  <body></body>\n"
 
842
                                                        "</html>\n", c->redirect_uri);
 
843
        return oidc_util_html_send(r, html, DONE);
 
844
}
 
845
 
 
846
/*
 
847
 * handle an error returned by the OP
 
848
 */
 
849
static int oidc_authorization_response_error(request_rec *r, oidc_cfg *c,
 
850
                oidc_proto_state *proto_state, const char *error,
 
851
                const char *error_description) {
 
852
        if ((proto_state->prompt != NULL)
 
853
                        && (apr_strnatcmp(proto_state->prompt, "none") == 0)) {
 
854
                return oidc_session_redirect_parent_window_to_logout(r, c);
 
855
        }
 
856
        return oidc_util_html_send_error(r, error, error_description, DONE);
 
857
}
767
858
 
768
859
/*
769
860
 * complete the handling of an authorization response by obtaining, parsing and verifying the
771
862
 */
772
863
static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
773
864
                session_rec *session, const char *state, char *code, char *id_token,
774
 
                char *access_token, char *token_type, const char *response_mode) {
 
865
                char *access_token, char *token_type, char *session_state,
 
866
                const char *error, const char *error_description,
 
867
                const char *response_mode) {
775
868
 
776
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
777
 
                        "oidc_handle_authorization_response: entering, state=%s, code=%s, id_token=%s, access_token=%s, token_type=%s",
778
 
                        state, code, id_token, access_token, token_type);
 
869
        oidc_debug(r,
 
870
                        "enter, state=%s, code=%s, id_token=%s, access_token=%s, token_type=%s, session_state=%s, error=%s, error_description=%s, response_mode=%s",
 
871
                        state, code, id_token, access_token, token_type, session_state,
 
872
                        error, error_description, response_mode);
779
873
 
780
874
        struct oidc_provider_t *provider = NULL;
781
875
        oidc_proto_state *proto_state = NULL;
786
880
                return HTTP_INTERNAL_SERVER_ERROR;
787
881
        }
788
882
 
 
883
        /* see if the response is an error response */
 
884
        if (error != NULL)
 
885
                return oidc_authorization_response_error(r, c, proto_state, error,
 
886
                                error_description);
 
887
 
789
888
        /* check the required response parameters for the requested flow */
790
 
        if (oidc_proto_validate_authorization_response(r, proto_state->response_type,
791
 
                        proto_state->response_mode, &code, &id_token, &access_token,
792
 
                        &token_type, response_mode) == FALSE) {
793
 
                return HTTP_INTERNAL_SERVER_ERROR;
 
889
        if (oidc_proto_validate_authorization_response(r,
 
890
                        proto_state->response_type, proto_state->response_mode, &code,
 
891
                        &id_token, &access_token, &token_type, response_mode) == FALSE) {
 
892
                return oidc_authorization_response_error(r, c, proto_state,
 
893
                                "HTTP_INTERNAL_SERVER_ERROR", NULL);
794
894
        }
795
895
 
796
896
        char *remoteUser = NULL;
800
900
        if (id_token != NULL) {
801
901
                if (oidc_proto_parse_idtoken(r, c, provider, id_token,
802
902
                                proto_state->nonce, &remoteUser, &jwt, FALSE) == FALSE) {
803
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
804
 
                                        "oidc_handle_authorization_response: could not parse or verify the id_token contents, return HTTP_UNAUTHORIZED");
805
 
                        return HTTP_UNAUTHORIZED;
 
903
                        oidc_warn(r,
 
904
                                        "could not parse or verify the id_token contents, return HTTP_UNAUTHORIZED");
 
905
                        return oidc_authorization_response_error(r, c, proto_state,
 
906
                                        "HTTP_UNAUTHORIZED", NULL);
806
907
                }
807
908
        }
808
909
 
814
915
                        if (oidc_proto_validate_code(r, provider, jwt,
815
916
                                        proto_state->response_type, code) == FALSE) {
816
917
                                apr_jwt_destroy(jwt);
817
 
                                return HTTP_UNAUTHORIZED;
 
918
                                return oidc_authorization_response_error(r, c, proto_state,
 
919
                                                "HTTP_UNAUTHORIZED", NULL);
818
920
                        }
819
921
                }
820
922
 
824
926
                if (oidc_proto_resolve_code(r, c, provider, code, &c_id_token,
825
927
                                &c_access_token, &c_token_type) == FALSE) {
826
928
                        apr_jwt_destroy(jwt);
827
 
                        return HTTP_UNAUTHORIZED;
 
929
                        return oidc_authorization_response_error(r, c, proto_state,
 
930
                                        "HTTP_UNAUTHORIZED", NULL);
828
931
                }
829
932
 
830
933
                /* validate the response on exchanging the code at the token endpoint */
831
 
                if (oidc_proto_validate_code_response(r,
832
 
                                proto_state->response_type, &c_id_token, &c_access_token,
833
 
                                &c_token_type) == FALSE) {
 
934
                if (oidc_proto_validate_code_response(r, proto_state->response_type,
 
935
                                &c_id_token, &c_access_token, &c_token_type) == FALSE) {
834
936
                        apr_jwt_destroy(jwt);
835
 
                        return HTTP_INTERNAL_SERVER_ERROR;
 
937
                        return oidc_authorization_response_error(r, c, proto_state,
 
938
                                        "HTTP_INTERNAL_SERVER_ERROR", NULL);
836
939
                }
837
940
 
838
941
                /* use from the response whatever we still need */
857
960
                if (jwt == NULL) {
858
961
                        if (oidc_proto_parse_idtoken(r, c, provider, id_token, nonce,
859
962
                                        &remoteUser, &jwt, TRUE) == FALSE) {
860
 
                                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
861
 
                                                "oidc_handle_authorization_response: could not parse or verify the id_token contents, return HTTP_UNAUTHORIZED");
862
 
                                return HTTP_UNAUTHORIZED;
 
963
                                oidc_warn(r,
 
964
                                                "could not parse or verify the id_token contents, return HTTP_UNAUTHORIZED");
 
965
                                return oidc_authorization_response_error(r, c, proto_state,
 
966
                                                "HTTP_UNAUTHORIZED", NULL);
863
967
                        }
864
968
                }
865
969
        }
866
970
 
867
971
        if (jwt == NULL) {
868
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
869
 
                                "oidc_handle_authorization_response: no id_token was provided, return HTTP_UNAUTHORIZED");
870
 
                return HTTP_UNAUTHORIZED;
 
972
                oidc_error(r, "no id_token was provided, return HTTP_UNAUTHORIZED");
 
973
                return oidc_authorization_response_error(r, c, proto_state,
 
974
                                "HTTP_UNAUTHORIZED", NULL);
871
975
        }
872
976
 
873
977
        /* validate the access token */
874
978
        if (access_token != NULL) {
875
979
                if (oidc_proto_validate_access_token(r, provider, jwt,
876
 
                                proto_state->response_type, access_token,
877
 
                                token_type) == FALSE) {
878
 
                        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
879
 
                                        "oidc_handle_authorization_response: access_token did not validate, dropping it");
 
980
                                proto_state->response_type, access_token, token_type) == FALSE) {
 
981
                        oidc_warn(r, "access_token did not validate, dropping it");
880
982
                        access_token = NULL;
881
983
                }
882
984
        }
887
989
         */
888
990
        const char *claims = NULL;
889
991
        json_t *j_claims = NULL;
890
 
        if (oidc_proto_resolve_userinfo(r, c, provider, access_token, &claims,
891
 
                        &j_claims) == FALSE) {
 
992
        if (provider->userinfo_endpoint_url == NULL) {
 
993
                oidc_debug(r,
 
994
                                "not resolving user info claims because userinfo_endpoint is not set");
 
995
        } else if (access_token == NULL) {
 
996
                oidc_debug(r,
 
997
                                "not resolving user info claims because access_token is not provided");
 
998
        } else if (oidc_proto_resolve_userinfo(r, c, provider, access_token,
 
999
                        &claims, &j_claims) == FALSE) {
 
1000
                oidc_debug(r,
 
1001
                                "resolving user info claims failed, nothing will be stored in the session");
892
1002
                claims = NULL;
893
1003
        }
894
1004
 
 
1005
        if ((proto_state->prompt != NULL)
 
1006
                        && (apr_strnatcmp(proto_state->prompt, "none") == 0)) {
 
1007
                // TOOD: actually need to compare sub? (need to store it in the session separately then
 
1008
                //const char *sub = NULL;
 
1009
                //oidc_session_get(r, session, "sub", &sub);
 
1010
                //if (apr_strnatcmp(sub, jwt->payload.sub) != 0) {
 
1011
                if (apr_strnatcmp(session->remote_user, remoteUser) != 0) {
 
1012
                        return oidc_authorization_response_error(r, c, proto_state,
 
1013
                                        "sub changed!", NULL);
 
1014
                }
 
1015
        }
 
1016
 
895
1017
        /* set the resolved stuff in the session */
896
1018
        session->remote_user = remoteUser;
897
1019
 
906
1028
        /* store the compact serialized representation of the id_token for later reference  */
907
1029
        oidc_session_set(r, session, OIDC_IDTOKEN_SESSION_KEY, id_token);
908
1030
 
 
1031
        if ((session_state != NULL) && (provider->check_session_iframe != NULL)) {
 
1032
                /* store the session state and required parameters session management  */
 
1033
                oidc_session_set(r, session, OIDC_SESSION_STATE_SESSION_KEY,
 
1034
                                session_state);
 
1035
                oidc_session_set(r, session, OIDC_CHECK_IFRAME_SESSION_KEY,
 
1036
                                provider->check_session_iframe);
 
1037
                oidc_session_set(r, session, OIDC_ISSUER_SESSION_KEY, provider->issuer);
 
1038
                oidc_session_set(r, session, OIDC_CLIENTID_SESSION_KEY,
 
1039
                                provider->client_id);
 
1040
        }
 
1041
 
 
1042
        if (provider->end_session_endpoint != NULL)
 
1043
                oidc_session_set(r, session, OIDC_LOGOUT_ENDPOINT_SESSION_KEY,
 
1044
                                provider->end_session_endpoint);
 
1045
 
909
1046
        /* see if we've resolved any claims */
910
1047
        if (claims != NULL) {
911
1048
                /*
919
1056
        /* see if we have an access_token */
920
1057
        if (access_token != NULL) {
921
1058
                /* store the access_token in the session context */
922
 
                oidc_session_set(r, session, OIDC_ACCESSTOKEN_SESSION_KEY, access_token);
 
1059
                oidc_session_set(r, session, OIDC_ACCESSTOKEN_SESSION_KEY,
 
1060
                                access_token);
923
1061
        }
924
1062
 
925
1063
        /* store the session */
932
1070
        if (apr_strnatcmp(proto_state->original_method, "form_post") == 0)
933
1071
                return oidc_restore_preserved_post(r, proto_state->original_url);
934
1072
 
 
1073
        /* log the successful response */
 
1074
        oidc_debug(r, "session created and stored, redirecting to original url: %s",
 
1075
                        proto_state->original_url);
 
1076
 
 
1077
        apr_jwt_destroy(jwt);
 
1078
        if (j_claims != NULL)
 
1079
                json_decref(j_claims);
 
1080
 
935
1081
        /* now we've authenticated the user so go back to the URL that he originally tried to access */
936
1082
        apr_table_add(r->headers_out, "Location", proto_state->original_url);
937
1083
 
938
 
        /* log the successful response */
939
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
940
 
                        "oidc_handle_authorization_response: session created and stored, redirecting to original url: %s",
941
 
                        proto_state->original_url);
942
 
 
943
 
        apr_jwt_destroy(jwt);
944
 
        if (j_claims != NULL) json_decref(j_claims);
945
 
 
946
1084
        /* do the actual redirect to the original URL */
947
1085
        return HTTP_MOVED_TEMPORARILY;
948
1086
}
953
1091
static int oidc_handle_post_authorization_response(request_rec *r, oidc_cfg *c,
954
1092
                session_rec *session) {
955
1093
 
956
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
957
 
                        "oidc_handle_post_authorization_response: entering");
 
1094
        oidc_debug(r, "enter");
958
1095
 
959
1096
        /* initialize local variables */
960
1097
        char *code = NULL, *state = NULL, *id_token = NULL, *access_token = NULL,
961
 
                        *token_type = NULL, *response_mode = NULL;
 
1098
                        *token_type = NULL, *response_mode = NULL, *session_state = NULL,
 
1099
                        *error = NULL, *error_description = NULL;
962
1100
 
963
1101
        /* read the parameters that are POST-ed to us */
964
1102
        apr_table_t *params = apr_table_make(r->pool, 8);
965
1103
        if (oidc_util_read_post(r, params) == FALSE) {
966
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
967
 
                                "oidc_handle_post_authorization_response: something went wrong when reading the POST parameters");
 
1104
                oidc_error(r, "something went wrong when reading the POST parameters");
968
1105
                return HTTP_INTERNAL_SERVER_ERROR;
969
1106
        }
970
1107
 
971
1108
        /* see if we've got any POST-ed data at all */
972
 
        if (apr_is_empty_table(params)) {
973
 
                return oidc_util_http_sendstring(r,
974
 
                                apr_psprintf(r->pool,
975
 
                                                "mod_auth_openidc: you've hit an OpenID Connect callback URL with no parameters; this is an invalid request (you should not open this URL in your browser directly)"),
 
1109
        if ((apr_table_elts(params)->nelts < 1)
 
1110
                        || ((apr_table_elts(params)->nelts == 1)
 
1111
                                        && (apr_strnatcmp(apr_table_get(params, "response_mode"),
 
1112
                                                        "fragment") == 0))) {
 
1113
                return oidc_util_html_send(r,
 
1114
                                "mod_auth_openidc: you've hit an OpenID Connect Redirect URI with no parameters, this is an invalid request; you should not open this URL in your browser directly, or have the server administrator use a different OIDCRedirectURI setting.",
976
1115
                                HTTP_INTERNAL_SERVER_ERROR);
977
1116
        }
978
1117
 
979
 
        /* see if the response is an error response */
980
 
        char *error = (char *) apr_table_get(params, "error");
981
 
        char *error_description = (char *) apr_table_get(params,
982
 
                        "error_description");
983
 
        if (error != NULL)
984
 
                return oidc_util_html_send_error(r, error, error_description, DONE);
985
 
 
986
1118
        /* get the parameters */
987
1119
        code = (char *) apr_table_get(params, "code");
988
1120
        state = (char *) apr_table_get(params, "state");
990
1122
        access_token = (char *) apr_table_get(params, "access_token");
991
1123
        token_type = (char *) apr_table_get(params, "token_type");
992
1124
        response_mode = (char *) apr_table_get(params, "response_mode");
 
1125
        session_state = (char *) apr_table_get(params, "session_state");
 
1126
        error = (char *) apr_table_get(params, "error");
 
1127
        error_description = (char *) apr_table_get(params, "error_description");
993
1128
 
994
1129
        /* do the actual implicit work */
995
1130
        return oidc_handle_authorization_response(r, c, session, state, code,
996
 
                        id_token, access_token, token_type, response_mode ? response_mode : "form_post");
 
1131
                        id_token, access_token, token_type, session_state, error,
 
1132
                        error_description, response_mode ? response_mode : "form_post");
997
1133
}
998
1134
 
999
1135
/*
1002
1138
static int oidc_handle_redirect_authorization_response(request_rec *r,
1003
1139
                oidc_cfg *c, session_rec *session) {
1004
1140
 
1005
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1006
 
                        "oidc_handle_redirect_authorization_response: entering");
 
1141
        oidc_debug(r, "enter");
1007
1142
 
1008
1143
        /* initialize local variables */
1009
1144
        char *code = NULL, *state = NULL, *id_token = NULL, *access_token = NULL,
1010
 
                        *token_type = NULL;
 
1145
                        *token_type = NULL, *session_state = NULL, *error = NULL,
 
1146
                        *error_description = NULL;
1011
1147
 
1012
1148
        /* get the parameters */
1013
1149
        oidc_util_get_request_parameter(r, "code", &code);
1015
1151
        oidc_util_get_request_parameter(r, "id_token", &id_token);
1016
1152
        oidc_util_get_request_parameter(r, "access_token", &access_token);
1017
1153
        oidc_util_get_request_parameter(r, "token_type", &token_type);
 
1154
        oidc_util_get_request_parameter(r, "session_state", &session_state);
 
1155
        oidc_util_get_request_parameter(r, "error", &error);
 
1156
        oidc_util_get_request_parameter(r, "error_description", &error_description);
1018
1157
 
1019
1158
        /* do the actual work */
1020
1159
        return oidc_handle_authorization_response(r, c, session, state, code,
1021
 
                        id_token, access_token, token_type, "query");
 
1160
                        id_token, access_token, token_type, session_state, error,
 
1161
                        error_description, "query");
1022
1162
}
1023
1163
 
1024
1164
/*
1026
1166
 */
1027
1167
static int oidc_discovery(request_rec *r, oidc_cfg *cfg) {
1028
1168
 
1029
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_discovery: entering");
 
1169
        oidc_debug(r, "enter");
1030
1170
 
1031
1171
        /* obtain the URL we're currently accessing, to be stored in the state/session */
1032
1172
        char *current_url = oidc_get_current_url(r, cfg);
1042
1182
                                oidc_util_escape_string(r, cfg->redirect_uri));
1043
1183
 
1044
1184
                /* log what we're about to do */
1045
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1046
 
                                "oidc_discovery: redirecting to external discovery page: %s",
1047
 
                                url);
 
1185
                oidc_debug(r, "redirecting to external discovery page: %s", url);
1048
1186
 
1049
1187
                /* do the actual redirect to an external discovery page */
1050
1188
                apr_table_add(r->headers_out, "Location", url);
1054
1192
        /* get a list of all providers configured in the metadata directory */
1055
1193
        apr_array_header_t *arr = NULL;
1056
1194
        if (oidc_metadata_list(r, cfg, &arr) == FALSE)
1057
 
                return oidc_util_http_sendstring(r,
 
1195
                return oidc_util_html_send(r,
1058
1196
                                "mod_auth_openidc: no configured providers found, contact your administrator",
1059
1197
                                HTTP_UNAUTHORIZED);
1060
1198
 
1117
1255
                        "</html>\n", s);
1118
1256
 
1119
1257
        /* now send the HTML contents to the user agent */
1120
 
        return oidc_util_http_sendstring(r, s, HTTP_UNAUTHORIZED);
 
1258
        return oidc_util_html_send(r, s, HTTP_UNAUTHORIZED);
1121
1259
}
1122
1260
 
1123
1261
/*
1124
1262
 * authenticate the user to the selected OP, if the OP is not selected yet perform discovery first
1125
1263
 */
1126
1264
static int oidc_authenticate_user(request_rec *r, oidc_cfg *c,
1127
 
                oidc_provider_t *provider, const char *original_url, const char *login_hint) {
 
1265
                oidc_provider_t *provider, const char *original_url,
 
1266
                const char *login_hint, const char *id_token_hint, const char *prompt,
 
1267
                const char *auth_request_params) {
1128
1268
 
1129
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1130
 
                        "oidc_authenticate_user: entering");
 
1269
        oidc_debug(r, "enter");
1131
1270
 
1132
1271
        if (provider == NULL) {
1133
1272
 
1136
1275
                        return oidc_discovery(r, c);
1137
1276
 
1138
1277
                /* we're not using multiple OP's configured in a metadata directory, pick the statically configured OP */
1139
 
                provider = &c->provider;
 
1278
                if (oidc_provider_static_config(r, c, &provider) == FALSE)
 
1279
                        return HTTP_INTERNAL_SERVER_ERROR;
1140
1280
        }
1141
1281
 
1142
1282
        /* generate a random value to correlate request/response through browser state */
1155
1295
        */
1156
1296
 
1157
1297
        /* create the state between request/response */
1158
 
        oidc_proto_state proto_state = { nonce, original_url, method, provider->issuer,
1159
 
                        provider->response_type, provider->response_mode, apr_time_sec(apr_time_now()) };
1160
 
 
1161
 
        /* create state that restores the context when the authorization response comes in; cryptographically bind it to the browser */
1162
 
        oidc_authorization_request_set_cookie(r, &proto_state);
 
1298
        oidc_proto_state proto_state = { nonce, original_url, method,
 
1299
                        provider->issuer, provider->response_type, provider->response_mode,
 
1300
                        prompt, apr_time_sec(apr_time_now()) };
1163
1301
 
1164
1302
        /* get a hash value that fingerprints the browser concatenated with the random input */
1165
1303
        char *state = oidc_get_browser_state_hash(r, proto_state.nonce);
1166
1304
 
 
1305
        /* create state that restores the context when the authorization response comes in; cryptographically bind it to the browser */
 
1306
        oidc_authorization_request_set_cookie(r, c, state, &proto_state);
 
1307
 
1167
1308
        /*
1168
1309
         * TODO: I'd like to include the nonce all flows, including the "code" and "code token" flows
1169
1310
         * but Google does not allow me to do that:
1170
1311
         * Error: invalid_request: Parameter not allowed for this message type: nonce
1171
1312
         */
1172
 
        if ((strcmp(provider->issuer, "accounts.google.com") == 0)
 
1313
        if ((apr_strnatcmp(provider->issuer, "accounts.google.com") == 0)
1173
1314
                        && ((oidc_util_spaced_string_equals(r->pool,
1174
1315
                                        provider->response_type, "code"))
1175
1316
                                        || (oidc_util_spaced_string_equals(r->pool,
1180
1321
         * printout errors if Cookie settings are not going to work
1181
1322
         */
1182
1323
        apr_uri_t o_uri;
 
1324
        memset(&o_uri, 0, sizeof(apr_uri_t));
1183
1325
        apr_uri_t r_uri;
 
1326
        memset(&r_uri, 0, sizeof(apr_uri_t));
1184
1327
        apr_uri_parse(r->pool, original_url, &o_uri);
1185
1328
        apr_uri_parse(r->pool, c->redirect_uri, &r_uri);
1186
 
        if ( (apr_strnatcmp(o_uri.scheme, r_uri.scheme) != 0) && (apr_strnatcmp(r_uri.scheme, "https") == 0) ) {
1187
 
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_authenticate_user: the URL scheme (%s) of the configured OIDCRedirectURI does not match the URL scheme of the URL being accessed (%s): the \"state\" and \"session\" cookies will not be shared between the two!", r_uri.scheme, o_uri.scheme);
 
1329
        if ((apr_strnatcmp(o_uri.scheme, r_uri.scheme) != 0)
 
1330
                        && (apr_strnatcmp(r_uri.scheme, "https") == 0)) {
 
1331
                oidc_error(r,
 
1332
                                "the URL scheme (%s) of the configured OIDCRedirectURI does not match the URL scheme of the URL being accessed (%s): the \"state\" and \"session\" cookies will not be shared between the two!",
 
1333
                                r_uri.scheme, o_uri.scheme);
1188
1334
        }
1189
1335
 
1190
1336
        if (c->cookie_domain == NULL) {
1191
1337
                if (apr_strnatcmp(o_uri.hostname, r_uri.hostname) != 0) {
1192
1338
                        char *p = strstr(o_uri.hostname, r_uri.hostname);
1193
 
                        if ( (p == NULL) || (apr_strnatcmp(r_uri.hostname, p) != 0)) {
1194
 
                                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_authenticate_user: the URL hostname (%s) of the configured OIDCRedirectURI does not match the URL hostname of the URL being accessed (%s): the \"state\" and \"session\" cookies will not be shared between the two!", r_uri.hostname, o_uri.hostname);
 
1339
                        if ((p == NULL) || (apr_strnatcmp(r_uri.hostname, p) != 0)) {
 
1340
                                oidc_error(r,
 
1341
                                                "the URL hostname (%s) of the configured OIDCRedirectURI does not match the URL hostname of the URL being accessed (%s): the \"state\" and \"session\" cookies will not be shared between the two!",
 
1342
                                                r_uri.hostname, o_uri.hostname);
1195
1343
                        }
1196
1344
                }
1197
1345
        } else {
1198
1346
                char *p = strstr(o_uri.hostname, c->cookie_domain);
1199
 
                if ( (p == NULL) || (apr_strnatcmp(c->cookie_domain, p) != 0)) {
1200
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_authenticate_user: the domain (%s) configured in OIDCCookieDomain does not match the URL hostname (%s) of the URL being accessed (%s): setting \"state\" and \"session\" cookies will not work!!", c->cookie_domain, o_uri.hostname, original_url);
 
1347
                if ((p == NULL) || (apr_strnatcmp(c->cookie_domain, p) != 0)) {
 
1348
                        oidc_error(r,
 
1349
                                        "the domain (%s) configured in OIDCCookieDomain does not match the URL hostname (%s) of the URL being accessed (%s): setting \"state\" and \"session\" cookies will not work!!",
 
1350
                                        c->cookie_domain, o_uri.hostname, original_url);
1201
1351
                }
1202
1352
        }
1203
1353
 
1204
1354
        /* send off to the OpenID Connect Provider */
1205
1355
        // TODO: maybe show intermediate/progress screen "redirecting to"
1206
 
        return oidc_proto_authorization_request(r, provider, login_hint, c->redirect_uri, state, &proto_state);
 
1356
        return oidc_proto_authorization_request(r, provider, login_hint,
 
1357
                        c->redirect_uri, state, &proto_state, id_token_hint,
 
1358
                        auth_request_params);
1207
1359
}
1208
1360
 
1209
1361
/*
1232
1384
                if (apr_strnatcmp(o_uri.hostname, r_uri.hostname) != 0) {
1233
1385
                        char *p = strstr(o_uri.hostname, r_uri.hostname);
1234
1386
                        if ((p == NULL) || (apr_strnatcmp(r_uri.hostname, p) != 0)) {
1235
 
                                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1236
 
                                                "oidc_target_link_uri_matches_configuration: the URL hostname (%s) of the configured OIDCRedirectURI does not match the URL hostname of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
 
1387
                                oidc_error(r,
 
1388
                                                "the URL hostname (%s) of the configured OIDCRedirectURI does not match the URL hostname of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
1237
1389
                                                r_uri.hostname, o_uri.hostname);
1238
1390
                                return FALSE;
1239
1391
                        }
1242
1394
                /* cookie_domain set: see if the target_link_uri is within the cookie_domain */
1243
1395
                char *p = strstr(o_uri.hostname, cfg->cookie_domain);
1244
1396
                if ((p == NULL) || (apr_strnatcmp(cfg->cookie_domain, p) != 0)) {
1245
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1246
 
                                        "oidc_target_link_uri_matches_configuration: the domain (%s) configured in OIDCCookieDomain does not match the URL hostname (%s) of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
 
1397
                        oidc_error(r,
 
1398
                                        "the domain (%s) configured in OIDCCookieDomain does not match the URL hostname (%s) of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
1247
1399
                                        cfg->cookie_domain, o_uri.hostname, target_link_uri);
1248
1400
                        return FALSE;
1249
1401
                }
1255
1407
        if (dir_cfg->cookie_path != NULL) {
1256
1408
                char *p = strstr(o_uri.path, dir_cfg->cookie_path);
1257
1409
                if ((p == NULL) || (p != o_uri.path)) {
1258
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1259
 
                                        "oidc_target_link_uri_matches_configuration: the path (%s) configured in OIDCCookiePath does not match the URL path (%s) of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
 
1410
                        oidc_error(r,
 
1411
                                        "the path (%s) configured in OIDCCookiePath does not match the URL path (%s) of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
1260
1412
                                        cfg->cookie_domain, o_uri.path, target_link_uri);
1261
1413
                        return FALSE;
1262
1414
                } else if (strlen(o_uri.path) > strlen(dir_cfg->cookie_path)) {
1264
1416
                        if (dir_cfg->cookie_path[n - 1] == '/')
1265
1417
                                n--;
1266
1418
                        if (o_uri.path[n] != '/') {
1267
 
                                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1268
 
                                                "oidc_target_link_uri_matches_configuration: the path (%s) configured in OIDCCookiePath does not match the URL path (%s) of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
 
1419
                                oidc_error(r,
 
1420
                                                "the path (%s) configured in OIDCCookiePath does not match the URL path (%s) of the \"target_link_uri\" (%s): aborting to prevent an open redirect.",
1269
1421
                                                cfg->cookie_domain, o_uri.path, target_link_uri);
1270
1422
                                return FALSE;
1271
1423
                        }
1280
1432
static int oidc_handle_discovery_response(request_rec *r, oidc_cfg *c) {
1281
1433
 
1282
1434
        /* variables to hold the values returned in the response */
1283
 
        char *issuer = NULL, *target_link_uri = NULL, *login_hint = NULL;
 
1435
        char *issuer = NULL, *target_link_uri = NULL, *login_hint = NULL,
 
1436
                        *auth_request_params = NULL;
1284
1437
        oidc_provider_t *provider = NULL;
1285
1438
 
1286
1439
        oidc_util_get_request_parameter(r, OIDC_DISC_OP_PARAM, &issuer);
1287
1440
        oidc_util_get_request_parameter(r, OIDC_DISC_RT_PARAM, &target_link_uri);
1288
1441
        oidc_util_get_request_parameter(r, OIDC_DISC_LH_PARAM, &login_hint);
 
1442
        oidc_util_get_request_parameter(r, OIDC_DISC_AR_PARAM,
 
1443
                        &auth_request_params);
1289
1444
 
1290
1445
        // TODO: trim issuer/accountname/domain input and do more input validation
1291
1446
 
1292
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1293
 
                        "oidc_handle_discovery_response: issuer=\"%s\", target_link_uri=\"%s\", login_hint=\"%s\"",
 
1447
        oidc_debug(r, "issuer=\"%s\", target_link_uri=\"%s\", login_hint=\"%s\"",
1294
1448
                        issuer, target_link_uri, login_hint);
1295
1449
 
1296
1450
        if (issuer == NULL) {
1297
 
                return oidc_util_http_sendstring(r,
 
1451
                return oidc_util_html_send(r,
1298
1452
                                "mod_auth_openidc: wherever you came from, it sent you here with the wrong parameters...",
1299
1453
                                HTTP_INTERNAL_SERVER_ERROR);
1300
1454
        }
1301
1455
 
1302
1456
        if (target_link_uri == NULL) {
1303
 
                if (c->default_url == NULL) {
1304
 
                        return oidc_util_http_sendstring(r,
1305
 
                                        "mod_auth_openidc: 3rd party initiated SSO to this module without specifying a \"target_link_uri\" parameter is not possible because OIDCDefaultURL is not set.",
 
1457
                if (c->default_sso_url == NULL) {
 
1458
                        return oidc_util_html_send(r,
 
1459
                                        "mod_auth_openidc: SSO to this module without specifying a \"target_link_uri\" parameter is not possible because OIDCDefaultURL is not set.",
1306
1460
                                        HTTP_INTERNAL_SERVER_ERROR);
1307
1461
                }
1308
 
                target_link_uri = c->default_url;
 
1462
                target_link_uri = c->default_sso_url;
1309
1463
        }
1310
1464
 
1311
1465
        /* do open redirect prevention */
1312
1466
        if (oidc_target_link_uri_matches_configuration(r, c,
1313
1467
                        target_link_uri) == FALSE) {
1314
 
                return oidc_util_http_sendstring(r,
 
1468
                return oidc_util_html_send(r,
1315
1469
                                "mod_auth_openidc: \"target_link_uri\" parameter does not match configuration settings, aborting to prevent an open redirect.",
1316
1470
                                HTTP_UNAUTHORIZED);
1317
1471
        }
1329
1483
                if (oidc_proto_account_based_discovery(r, c, issuer, &issuer) == FALSE) {
1330
1484
 
1331
1485
                        /* something did not work out, show a user facing error */
1332
 
                        return oidc_util_http_sendstring(r,
 
1486
                        return oidc_util_html_send(r,
1333
1487
                                        "mod_auth_openidc: could not resolve the provided account name to an OpenID Connect provider; check your syntax",
1334
1488
                                        HTTP_NOT_FOUND);
1335
1489
                }
1355
1509
                        && (provider != NULL)) {
1356
1510
 
1357
1511
                /* now we've got a selected OP, send the user there to authenticate */
1358
 
                return oidc_authenticate_user(r, c, provider, target_link_uri, login_hint);
 
1512
                return oidc_authenticate_user(r, c, provider, target_link_uri,
 
1513
                                login_hint, NULL, NULL, auth_request_params);
1359
1514
        }
1360
1515
 
1361
1516
        /* something went wrong */
1362
 
        return oidc_util_http_sendstring(r,
 
1517
        return oidc_util_html_send(r,
1363
1518
                        "mod_auth_openidc: could not find valid provider metadata for the selected OpenID Connect provider; contact the administrator",
1364
1519
                        HTTP_NOT_FOUND);
1365
1520
}
1366
1521
 
1367
1522
/*
1368
 
 * kill session
 
1523
 * handle a local logout
1369
1524
 */
1370
 
int oidc_handle_logout(request_rec *r, session_rec *session) {
1371
 
        char *url = NULL;
 
1525
static int oidc_handle_logout_request(request_rec *r, oidc_cfg *c,
 
1526
                session_rec *session, const char *url) {
 
1527
 
 
1528
        oidc_debug(r, "enter (url=%s)", url);
1372
1529
 
1373
1530
        /* if there's no remote_user then there's no (stored) session to kill */
1374
1531
        if (session->remote_user != NULL) {
1377
1534
                oidc_session_kill(r, session);
1378
1535
        }
1379
1536
 
1380
 
        /* pickup the URL where the user wants to go after logout */
 
1537
        if (url == NULL) {
 
1538
                char *html =
 
1539
                                "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
 
1540
                                                "<html>\n"
 
1541
                                                "  <head>\n"
 
1542
                                                "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
 
1543
                                                "  </head>\n"
 
1544
                                                "  <body>\n"
 
1545
                                                "    <p>Logged Out</p>\n"
 
1546
                                                "  </body>\n"
 
1547
                                                "</html>\n";
 
1548
                return oidc_util_html_send(r, html, DONE);
 
1549
        }
 
1550
 
 
1551
        /* send the user to the specified where-to-go-after-logout URL */
 
1552
        apr_table_add(r->headers_out, "Location", url);
 
1553
 
 
1554
        return HTTP_MOVED_TEMPORARILY;
 
1555
}
 
1556
 
 
1557
/*
 
1558
 * perform (single) logout
 
1559
 */
 
1560
static int oidc_handle_logout(request_rec *r, oidc_cfg *c, session_rec *session) {
 
1561
 
 
1562
        /* pickup the command or URL where the user wants to go after logout */
 
1563
        char *url = NULL;
1381
1564
        oidc_util_get_request_parameter(r, "logout", &url);
1382
 
 
1383
 
        /* send him there */
1384
 
        apr_table_add(r->headers_out, "Location", url);
1385
 
        return HTTP_MOVED_TEMPORARILY;
 
1565
        if ((url == NULL) || (apr_strnatcmp(url, "") == 0))
 
1566
                url = c->default_sso_url;
 
1567
 
 
1568
        oidc_debug(r, "enter (url=%s)", url);
 
1569
 
 
1570
        apr_uri_t uri;
 
1571
        if ((url != NULL) && (apr_uri_parse(r->pool, url, &uri) != APR_SUCCESS)) {
 
1572
                const char *error_description = apr_psprintf(r->pool,
 
1573
                                "Logout URL malformed: %s", url);
 
1574
                oidc_error(r, "%s", error_description);
 
1575
                return oidc_util_html_send_error(r, url, error_description,
 
1576
                                HTTP_INTERNAL_SERVER_ERROR);
 
1577
        }
 
1578
 
 
1579
        const char *end_session_endpoint = NULL;
 
1580
        oidc_session_get(r, session, OIDC_LOGOUT_ENDPOINT_SESSION_KEY,
 
1581
                        &end_session_endpoint);
 
1582
        if (end_session_endpoint != NULL) {
 
1583
 
 
1584
                const char *id_token_hint = NULL;
 
1585
                oidc_session_get(r, session, OIDC_IDTOKEN_SESSION_KEY, &id_token_hint);
 
1586
 
 
1587
                char *logout_request = apr_psprintf(r->pool, "%s%s",
 
1588
                                end_session_endpoint,
 
1589
                                strchr(end_session_endpoint, '?') != NULL ? "&" : "?");
 
1590
                logout_request = apr_psprintf(r->pool, "%sid_token_hint=%s",
 
1591
                                logout_request, oidc_util_escape_string(r, id_token_hint));
 
1592
 
 
1593
                if (url != NULL) {
 
1594
                        logout_request = apr_psprintf(r->pool,
 
1595
                                        "%s&post_logout_redirect_uri=%s", logout_request,
 
1596
                                        oidc_util_escape_string(r, url));
 
1597
                }
 
1598
                url = logout_request;
 
1599
        }
 
1600
 
 
1601
        return oidc_handle_logout_request(r, c, session, url);
1386
1602
}
1387
1603
 
1388
1604
/*
1389
1605
 * handle request for JWKs
1390
1606
 */
1391
 
int oidc_handle_jwks(request_rec *r, oidc_cfg *c) {
 
1607
static int oidc_handle_jwks(request_rec *r, oidc_cfg *c) {
1392
1608
 
1393
1609
        /* pickup requested JWKs type */
1394
1610
//      char *jwks_type = NULL;
1395
1611
//      oidc_util_get_request_parameter(r, "jwks", &jwks_type);
1396
 
 
1397
1612
        char *jwks = apr_pstrdup(r->pool, "{ \"keys\" : [");
1398
1613
        apr_hash_index_t *hi = NULL;
1399
1614
        apr_byte_t first = TRUE;
1400
1615
        /* loop over the claims in the JSON structure */
1401
1616
        if (c->public_keys != NULL) {
1402
 
                for (hi = apr_hash_first(r->pool, c->public_keys); hi; hi = apr_hash_next(hi)) {
 
1617
                for (hi = apr_hash_first(r->pool, c->public_keys); hi; hi =
 
1618
                                apr_hash_next(hi)) {
1403
1619
                        const char *s_kid = NULL;
1404
1620
                        const char *s_jwk = NULL;
1405
1621
                        apr_hash_this(hi, (const void**) &s_kid, NULL, (void**) &s_jwk);
1406
 
                        jwks = apr_psprintf(r->pool, "%s%s %s ", jwks, first ? "" : ",", s_jwk);
 
1622
                        jwks = apr_psprintf(r->pool, "%s%s %s ", jwks, first ? "" : ",",
 
1623
                                        s_jwk);
1407
1624
                        first = FALSE;
1408
1625
                }
1409
1626
        }
1411
1628
        // TODO: send stuff if first == FALSE?
1412
1629
        jwks = apr_psprintf(r->pool, "%s ] }", jwks);
1413
1630
 
1414
 
        return oidc_util_http_sendstring(r, jwks, DONE);
 
1631
        return oidc_util_http_send(r, jwks, strlen(jwks), "application/json", DONE);
 
1632
}
 
1633
 
 
1634
static int oidc_handle_session_management_iframe_op(request_rec *r, oidc_cfg *c,
 
1635
                session_rec *session, const char *check_session_iframe) {
 
1636
 
 
1637
        oidc_debug(r, "enter");
 
1638
 
 
1639
        if (check_session_iframe == NULL) {
 
1640
                oidc_debug(r, "no check_session_iframe configured for current OP");
 
1641
                return DONE;
 
1642
        }
 
1643
 
 
1644
        apr_table_add(r->headers_out, "Location", check_session_iframe);
 
1645
        return HTTP_MOVED_TEMPORARILY;
 
1646
}
 
1647
 
 
1648
static int oidc_handle_session_management_iframe_rp(request_rec *r, oidc_cfg *c,
 
1649
                session_rec *session, const char *client_id,
 
1650
                const char *check_session_iframe) {
 
1651
 
 
1652
        oidc_debug(r, "enter");
 
1653
 
 
1654
        const char *iframe_contents =
 
1655
                        "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
 
1656
                                        "<html>\n"
 
1657
                                        "  <head>\n"
 
1658
                                        "   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"
 
1659
                                        "    <script type=\"text/javascript\">\n"
 
1660
                                        "      var targetOrigin  = '%s';\n"
 
1661
                                        "      var message = '%s' + ' ' + '%s';\n"
 
1662
                                        "          var timerID;\n"
 
1663
                                        "\n"
 
1664
                                        "      function checkSession() {\n"
 
1665
                                        "        console.log('checkSession: posting ' + message + ' to ' + targetOrigin);\n"
 
1666
                                        "        var win = window.parent.document.getElementById('%s').contentWindow;\n"
 
1667
                                        "        win.postMessage( message, targetOrigin);\n"
 
1668
                                        "      }\n"
 
1669
                                        "\n"
 
1670
                                        "      function setTimer() {\n"
 
1671
                                        "        checkSession();\n"
 
1672
                                        "        timerID = setInterval('checkSession()', %s);\n"
 
1673
                                        "      }\n"
 
1674
                                        "\n"
 
1675
                                        "      function receiveMessage(e) {\n"
 
1676
                                        "        console.log('receiveMessage: ' + e.data + ' from ' + e.origin);\n"
 
1677
                                        "        if (e.origin !== targetOrigin ) {\n"
 
1678
                                        "          console.log('receiveMessage: cross-site scripting attack?');\n"
 
1679
                                        "          return;\n"
 
1680
                                        "        }\n"
 
1681
                                        "        if (e.data != 'unchanged') {\n"
 
1682
                                        "          clearInterval(timerID);\n"
 
1683
                                        "          if (e.data == 'changed') {\n"
 
1684
                                        "                    window.location.href = '%s?session=check';\n"
 
1685
                                        "          } else {\n"
 
1686
                                        "                    window.location.href = '%s?session=logout';\n"
 
1687
                                        "          }\n"
 
1688
                                        "        }\n"
 
1689
                                        "      }\n"
 
1690
                                        "\n"
 
1691
                                        "      window.addEventListener('message', receiveMessage, false);\n"
 
1692
                                        "\n"
 
1693
                                        "    </script>\n"
 
1694
                                        "  </head>\n"
 
1695
                                        "  <body onload=\"setTimer()\"><p></p></body>\n"
 
1696
                                        "</html>\n";
 
1697
 
 
1698
        /* determine the origin for the check_session_iframe endpoint */
 
1699
        char *origin = apr_pstrdup(r->pool, check_session_iframe);
 
1700
        apr_uri_t uri;
 
1701
        apr_uri_parse(r->pool, check_session_iframe, &uri);
 
1702
        char *p = strstr(origin, uri.path);
 
1703
        *p = '\0';
 
1704
 
 
1705
        /* the element identifier for the OP iframe */
 
1706
        const char *op_iframe_id = "openidc-op";
 
1707
 
 
1708
        /* restore the OP session_state from the session */
 
1709
        const char *session_state = NULL;
 
1710
        oidc_session_get(r, session, OIDC_SESSION_STATE_SESSION_KEY,
 
1711
                        &session_state);
 
1712
        if (session_state == NULL) {
 
1713
                oidc_warn(r,
 
1714
                                "no session_state found in the session; the OP does probably not support session management!?");
 
1715
                return DONE;
 
1716
        }
 
1717
 
 
1718
        char *s_poll_interval = NULL;
 
1719
        oidc_util_get_request_parameter(r, "poll", &s_poll_interval);
 
1720
        if (s_poll_interval == NULL)
 
1721
                s_poll_interval = "3000";
 
1722
        iframe_contents = apr_psprintf(r->pool, iframe_contents, origin, client_id,
 
1723
                        session_state, op_iframe_id, s_poll_interval, c->redirect_uri,
 
1724
                        c->redirect_uri);
 
1725
 
 
1726
        return oidc_util_html_send(r, iframe_contents, DONE);
 
1727
}
 
1728
 
 
1729
/*
 
1730
 * handle session management request
 
1731
 */
 
1732
static int oidc_handle_session_management(request_rec *r, oidc_cfg *c,
 
1733
                session_rec *session) {
 
1734
        char *cmd = NULL;
 
1735
        const char *issuer = NULL, *id_token_hint = NULL, *client_id = NULL,
 
1736
                        *check_session_iframe = NULL;
 
1737
        oidc_provider_t *provider = NULL;
 
1738
 
 
1739
        /* get the command passed to the session management handler */
 
1740
        oidc_util_get_request_parameter(r, "session", &cmd);
 
1741
        if (cmd == NULL) {
 
1742
                oidc_error(r, "session management handler called with no command");
 
1743
                return HTTP_INTERNAL_SERVER_ERROR;
 
1744
        }
 
1745
 
 
1746
        /* see if this is a local logout during session management */
 
1747
        if (apr_strnatcmp("logout", cmd) == 0) {
 
1748
                oidc_debug(r,
 
1749
                                "[session=logout] calling oidc_handle_logout_request because of session mgmt local logout call.");
 
1750
                return oidc_handle_logout_request(r, c, session, c->default_slo_url);
 
1751
        }
 
1752
 
 
1753
        /* see if this is a request for the OP iframe */
 
1754
        if (apr_strnatcmp("iframe_op", cmd) == 0) {
 
1755
                oidc_session_get(r, session, OIDC_CHECK_IFRAME_SESSION_KEY,
 
1756
                                &check_session_iframe);
 
1757
                if (check_session_iframe != NULL) {
 
1758
                        return oidc_handle_session_management_iframe_op(r, c, session,
 
1759
                                        check_session_iframe);
 
1760
                }
 
1761
                return DONE;
 
1762
        }
 
1763
 
 
1764
        /* see if this is a request for the RP iframe */
 
1765
        if (apr_strnatcmp("iframe_rp", cmd) == 0) {
 
1766
                oidc_session_get(r, session, OIDC_CLIENTID_SESSION_KEY, &client_id);
 
1767
                oidc_session_get(r, session, OIDC_CHECK_IFRAME_SESSION_KEY,
 
1768
                                &check_session_iframe);
 
1769
                if ((client_id != NULL) && (check_session_iframe != NULL)) {
 
1770
                        return oidc_handle_session_management_iframe_rp(r, c, session,
 
1771
                                        client_id, check_session_iframe);
 
1772
                }
 
1773
                return DONE;
 
1774
        }
 
1775
 
 
1776
        /* see if this is a request check the login state with the OP */
 
1777
        if (apr_strnatcmp("check", cmd) == 0) {
 
1778
                oidc_session_get(r, session, OIDC_IDTOKEN_SESSION_KEY, &id_token_hint);
 
1779
                oidc_session_get(r, session, OIDC_ISSUER_SESSION_KEY, &issuer);
 
1780
                if (issuer != NULL)
 
1781
                        provider = oidc_get_provider_for_issuer(r, c, issuer);
 
1782
                if ((id_token_hint != NULL) && (provider != NULL)) {
 
1783
                        return oidc_authenticate_user(r, c, provider,
 
1784
                                        apr_psprintf(r->pool, "%s?session=iframe_rp",
 
1785
                                                        c->redirect_uri), NULL, id_token_hint, "none", NULL);
 
1786
                }
 
1787
                oidc_debug(r,
 
1788
                                "[session=check] calling oidc_handle_logout_request because no session found.");
 
1789
                return oidc_session_redirect_parent_window_to_logout(r, c);
 
1790
        }
 
1791
 
 
1792
        /* handle failure in fallthrough */
 
1793
        oidc_error(r, "unknown command: %s", cmd);
 
1794
 
 
1795
        return HTTP_INTERNAL_SERVER_ERROR;
1415
1796
}
1416
1797
 
1417
1798
/*
1438
1819
        } else if (oidc_util_request_has_parameter(r, "logout")) {
1439
1820
 
1440
1821
                /* handle logout */
1441
 
                return oidc_handle_logout(r, session);
 
1822
                return oidc_handle_logout(r, c, session);
1442
1823
 
1443
1824
        } else if (oidc_util_request_has_parameter(r, "jwks")) {
1444
1825
 
1445
1826
                /* handle JWKs request */
1446
1827
                return oidc_handle_jwks(r, c);
1447
1828
 
1448
 
        }  else if ((r->args == NULL) || (apr_strnatcmp(r->args, "") == 0)) {
 
1829
        } else if (oidc_util_request_has_parameter(r, "session")) {
 
1830
 
 
1831
                /* handle session management request */
 
1832
                return oidc_handle_session_management(r, c, session);
 
1833
 
 
1834
        } else if ((r->args == NULL) || (apr_strnatcmp(r->args, "") == 0)) {
1449
1835
 
1450
1836
                /* this is a "bare" request to the redirect URI, indicating implicit flow using the fragment response_mode */
1451
1837
                return oidc_proto_javascript_implicit(r, c);
1456
1842
        /* check for "error" response */
1457
1843
        if (oidc_util_request_has_parameter(r, "error")) {
1458
1844
 
1459
 
                char *error = NULL, *descr = NULL;
1460
 
                oidc_util_get_request_parameter(r, "error", &error);
1461
 
                oidc_util_get_request_parameter(r, "error_description", &descr);
1462
 
 
1463
 
                /* send user facing error to browser */
1464
 
                return oidc_util_html_send_error(r, error, descr, DONE);
 
1845
//              char *error = NULL, *descr = NULL;
 
1846
//              oidc_util_get_request_parameter(r, "error", &error);
 
1847
//              oidc_util_get_request_parameter(r, "error_description", &descr);
 
1848
//
 
1849
//              /* send user facing error to browser */
 
1850
//              return oidc_util_html_send_error(r, error, descr, DONE);
 
1851
                oidc_handle_redirect_authorization_response(r, c, session);
1465
1852
        }
1466
1853
 
1467
1854
        /* something went wrong */
1468
 
        return oidc_util_http_sendstring(r,
 
1855
        return oidc_util_html_send(r,
1469
1856
                        apr_psprintf(r->pool,
1470
1857
                                        "mod_auth_openidc: the OpenID Connect callback URL received an invalid request: %s",
1471
1858
                                        r->args), HTTP_INTERNAL_SERVER_ERROR);
1514
1901
                if (r->user != NULL) {
1515
1902
 
1516
1903
                        /* this is a sub-request and we have a session (headers will have been scrubbed and set already) */
1517
 
                        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1518
 
                                        "oidc_check_userid_openid_openidc: recycling user '%s' from initial request for sub-request",
 
1904
                        oidc_debug(r,
 
1905
                                        "recycling user '%s' from initial request for sub-request",
1519
1906
                                        r->user);
1520
1907
 
1521
1908
                        return OK;
1527
1914
        }
1528
1915
 
1529
1916
        /* no session (regardless of whether it is main or sub-request), go and authenticate the user */
1530
 
        return oidc_authenticate_user(r, c, NULL, oidc_get_current_url(r, c), NULL);
 
1917
        return oidc_authenticate_user(r, c, NULL, oidc_get_current_url(r, c), NULL,
 
1918
                        NULL, NULL, NULL);
1531
1919
}
1532
1920
 
1533
1921
/*
1539
1927
                        &auth_openidc_module);
1540
1928
 
1541
1929
        /* log some stuff about the incoming HTTP request */
1542
 
        ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1543
 
                        "oidc_check_user_id: incoming request: \"%s?%s\", ap_is_initial_req(r)=%d",
 
1930
        oidc_debug(r, "incoming request: \"%s?%s\", ap_is_initial_req(r)=%d",
1544
1931
                        r->parsed_uri.path, r->args, ap_is_initial_req(r));
1545
1932
 
1546
1933
        /* see if any authentication has been defined at all */
1563
1950
/*
1564
1951
 * get the claims and id_token from request state
1565
1952
 */
1566
 
static void oidc_authz_get_claims_and_idtoken(request_rec *r, json_t **claims, json_t **id_token) {
 
1953
static void oidc_authz_get_claims_and_idtoken(request_rec *r, json_t **claims,
 
1954
                json_t **id_token) {
1567
1955
        const char *s_claims = oidc_request_state_get(r, OIDC_CLAIMS_SESSION_KEY);
1568
 
        const char *s_id_token = oidc_request_state_get(r, OIDC_IDTOKEN_CLAIMS_SESSION_KEY);
 
1956
        const char *s_id_token = oidc_request_state_get(r,
 
1957
                        OIDC_IDTOKEN_CLAIMS_SESSION_KEY);
1569
1958
        json_error_t json_error;
1570
1959
        if (s_claims != NULL) {
1571
1960
                *claims = json_loads(s_claims, 0, &json_error);
1572
1961
                if (*claims == NULL) {
1573
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1574
 
                                        "oidc_authz_get_claims_and_idtoken: could not restore claims from request state: %s", json_error.text);
 
1962
                        oidc_error(r, "could not restore claims from request state: %s",
 
1963
                                        json_error.text);
1575
1964
                }
1576
1965
        }
1577
1966
        if (s_id_token != NULL) {
1578
1967
                *id_token = json_loads(s_id_token, 0, &json_error);
1579
1968
                if (*id_token == NULL) {
1580
 
                        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1581
 
                                        "oidc_authz_get_claims_and_idtoken: could not restore id_token from request state: %s", json_error.text);
 
1969
                        oidc_error(r, "could not restore id_token from request state: %s",
 
1970
                                        json_error.text);
1582
1971
                }
1583
1972
        }
1584
1973
}
1621
2010
        const require_line * const reqs =
1622
2011
                        reqs_arr ? (require_line *) reqs_arr->elts : NULL;
1623
2012
        if (!reqs_arr) {
1624
 
                ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1625
 
                                "No require statements found, "
1626
 
                                "so declining to perform authorization.");
 
2013
                oidc_debug(r,
 
2014
                                "no require statements found, so declining to perform authorization.");
1627
2015
                return DECLINED;
1628
2016
        }
1629
2017
 
1630
2018
        /* dispatch to the <2.4 specific authz routine */
1631
 
        int rc = oidc_authz_worker(r, claims ? claims : id_token, reqs, reqs_arr->nelts);
 
2019
        int rc = oidc_authz_worker(r, claims ? claims : id_token, reqs,
 
2020
                        reqs_arr->nelts);
1632
2021
 
1633
2022
        /* cleanup */
1634
 
        if (claims) json_decref(claims);
1635
 
        if (id_token) json_decref(id_token);
 
2023
        if (claims)
 
2024
                json_decref(claims);
 
2025
        if (id_token)
 
2026
                json_decref(id_token);
1636
2027
 
1637
2028
        return rc;
1638
2029
}