1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 /***************************************************************************
21  * Copyright (C) 2017-2021 ZmartZone Holding BV
22  * Copyright (C) 2013-2017 Ping Identity Corporation
23  * All rights reserved.
24  *
25  * DISCLAIMER OF WARRANTIES:
26  *
27  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
28  * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
29  * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
30  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
31  * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
32  * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
33  * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
34  * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
35  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
37  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
42  */
43 
44 #include <apr_base64.h>
45 #include <apr_lib.h>
46 
47 #include <httpd.h>
48 #include <http_core.h>
49 #include <http_config.h>
50 #include <http_log.h>
51 
52 #include "mod_auth_openidc.h"
53 
54 extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
55 
56 /* the name of the remote-user attribute in the session  */
57 #define OIDC_SESSION_REMOTE_USER_KEY              "r"
58 /* the name of the session expiry attribute in the session */
59 #define OIDC_SESSION_EXPIRY_KEY                   "e"
60 /* the name of the provided token binding attribute in the session */
61 #define OIDC_SESSION_PROVIDED_TOKEN_BINDING_KEY   "ptb"
62 /* the name of the session identifier in the session */
63 #define OIDC_SESSION_SESSION_ID                   "i"
64 /* the name of the sub attribute in the session */
65 #define OIDC_SESSION_SUB_KEY                      "sub"
66 /* the name of the sid attribute in the session */
67 #define OIDC_SESSION_SID_KEY                      "sid"
68 
oidc_session_encode(request_rec * r,oidc_cfg * c,oidc_session_t * z,char ** s_value,apr_byte_t encrypt)69 static apr_byte_t oidc_session_encode(request_rec *r, oidc_cfg *c,
70 		oidc_session_t *z, char **s_value, apr_byte_t encrypt) {
71 
72 	if (encrypt == FALSE) {
73 		*s_value = oidc_util_encode_json_object(r, z->state, JSON_COMPACT);
74 		return (*s_value != NULL);
75 	}
76 
77 	if (oidc_util_jwt_create(r, c->crypto_passphrase, z->state,
78 			s_value) == FALSE)
79 		return FALSE;
80 
81 	return TRUE;
82 }
83 
oidc_session_decode(request_rec * r,oidc_cfg * c,oidc_session_t * z,const char * s_json,apr_byte_t encrypt)84 static apr_byte_t oidc_session_decode(request_rec *r, oidc_cfg *c,
85 		oidc_session_t *z, const char *s_json, apr_byte_t encrypt) {
86 
87 	if (encrypt == FALSE) {
88 		return oidc_util_decode_json_object(r, s_json, &z->state);
89 	}
90 
91 	if (oidc_util_jwt_verify(r, c->crypto_passphrase, s_json,
92 			&z->state) == FALSE) {
93 		oidc_error(r,
94 				"could not verify secure JWT: cache value possibly corrupted");
95 		return FALSE;
96 	}
97 	return TRUE;
98 }
99 
100 /*
101  * generate a unique identifier for a session
102  */
oidc_session_uuid_new(request_rec * r,oidc_session_t * z)103 static void oidc_session_uuid_new(request_rec *r, oidc_session_t *z) {
104 	apr_uuid_t uuid;
105 	apr_uuid_get(&uuid);
106 	apr_uuid_format((char *) &z->uuid, &uuid);
107 }
108 
109 /*
110  * clear contents of a session
111  */
oidc_session_clear(request_rec * r,oidc_session_t * z)112 static void oidc_session_clear(request_rec *r, oidc_session_t *z) {
113 	z->uuid[0] = '\0';
114 	z->remote_user = NULL;
115 	// NB: don't clear sid
116 	z->expiry = 0;
117 	if (z->state) {
118 		json_decref(z->state);
119 		z->state = NULL;
120 	}
121 }
122 
oidc_session_load_cache_by_uuid(request_rec * r,oidc_cfg * c,const char * uuid,oidc_session_t * z)123 apr_byte_t oidc_session_load_cache_by_uuid(request_rec *r, oidc_cfg *c,
124 		const char *uuid, oidc_session_t *z) {
125 	const char *stored_uuid = NULL;
126 	char *s_json = NULL;
127 	apr_byte_t rc = FALSE;
128 
129 	rc = oidc_cache_get_session(r, uuid, &s_json);
130 
131 	if ((rc == TRUE) && (s_json != NULL)) {
132 		rc = oidc_session_decode(r, c, z, s_json, FALSE);
133 		if (rc == TRUE) {
134 			strncpy(z->uuid, uuid, APR_UUID_FORMATTED_LENGTH);
135 			z->uuid[APR_UUID_FORMATTED_LENGTH] = '\0';
136 
137 			/* compare the session id in the cache value so it allows  us to detect cache corruption */
138 			oidc_session_get(r, z, OIDC_SESSION_SESSION_ID, &stored_uuid);
139 			if ((stored_uuid == NULL)
140 					|| (apr_strnatcmp(stored_uuid, uuid) != 0)) {
141 				oidc_error(r,
142 						"cache corruption detected: stored session id (%s) is not equal to requested session id (%s)",
143 						stored_uuid, uuid);
144 
145 				/* delete the cache entry */
146 				oidc_cache_set_session(r, z->uuid, NULL, 0);
147 				/* clear the session */
148 				oidc_session_clear(r, z);
149 
150 				rc = FALSE;
151 			}
152 		}
153 	}
154 
155 	return rc;
156 }
157 
158 /*
159  * load the session from the cache using the cookie as the index
160  */
oidc_session_load_cache(request_rec * r,oidc_session_t * z)161 static apr_byte_t oidc_session_load_cache(request_rec *r, oidc_session_t *z) {
162 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
163 			&auth_openidc_module);
164 
165 	apr_byte_t rc = FALSE;
166 
167 	/* get the cookie that should be our uuid/key */
168 	char *uuid = oidc_util_get_cookie(r, oidc_cfg_dir_cookie(r));
169 
170 	/* get the string-encoded session from the cache based on the key; decryption is based on the cache backend config */
171 	if (uuid != NULL) {
172 
173 		rc = oidc_session_load_cache_by_uuid(r, c, uuid, z);
174 
175 		if (rc == FALSE || z->state == NULL) {
176 			/* delete the session cookie */
177 			oidc_util_set_cookie(r, oidc_cfg_dir_cookie(r), "", 0,
178 					OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
179 		}
180 	}
181 
182 	return rc;
183 }
184 
185 /*
186  * save the session to the cache using a cookie for the index
187  */
oidc_session_save_cache(request_rec * r,oidc_session_t * z,apr_byte_t first_time)188 static apr_byte_t oidc_session_save_cache(request_rec *r, oidc_session_t *z,
189 		apr_byte_t first_time) {
190 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
191 			&auth_openidc_module);
192 
193 	apr_byte_t rc = TRUE;
194 
195 	if (z->state != NULL) {
196 
197 		if (apr_strnatcmp(z->uuid, "") == 0) {
198 			/* get a new uuid for this session */
199 			oidc_session_uuid_new(r, z);
200 			/* store the session id in the cache value so it allows  us to detect cache corruption */
201 			oidc_session_set(r, z, OIDC_SESSION_SESSION_ID, z->uuid);
202 		}
203 
204 		if (z->sid != NULL) {
205 			oidc_cache_set_sid(r, z->sid, z->uuid, z->expiry);
206 			oidc_session_set(r, z, OIDC_SESSION_SID_KEY, z->sid);
207 		}
208 
209 		/* store the string-encoded session in the cache; encryption depends on cache backend settings */
210 		char *s_value = NULL;
211 		if (oidc_session_encode(r, c, z, &s_value, FALSE) == FALSE)
212 			return FALSE;
213 		rc = oidc_cache_set_session(r, z->uuid, s_value, z->expiry);
214 
215 		if (rc == TRUE)
216 			/* set the uuid in the cookie */
217 			oidc_util_set_cookie(r, oidc_cfg_dir_cookie(r), z->uuid,
218 					c->persistent_session_cookie ? z->expiry : -1,
219 							c->cookie_same_site ?
220 									(first_time ?
221 											OIDC_COOKIE_EXT_SAME_SITE_LAX :
222 											OIDC_COOKIE_EXT_SAME_SITE_STRICT) :
223 											OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
224 
225 	} else {
226 
227 		if (z->sid != NULL)
228 			oidc_cache_set_sid(r, z->sid, NULL, 0);
229 
230 		/* clear the cookie */
231 		oidc_util_set_cookie(r, oidc_cfg_dir_cookie(r), "", 0,
232 				OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
233 
234 		/* remove the session from the cache */
235 		rc = oidc_cache_set_session(r, z->uuid, NULL, 0);
236 	}
237 
238 	return rc;
239 }
240 
241 /*
242  * load the session from a self-contained client-side cookie
243  */
oidc_session_load_cookie(request_rec * r,oidc_cfg * c,oidc_session_t * z)244 static apr_byte_t oidc_session_load_cookie(request_rec *r, oidc_cfg *c,
245 		oidc_session_t *z) {
246 	char *cookieValue = oidc_util_get_chunked_cookie(r, oidc_cfg_dir_cookie(r),
247 			c->session_cookie_chunk_size);
248 	if ((cookieValue != NULL)
249 			&& (oidc_session_decode(r, c, z, cookieValue, TRUE) == FALSE))
250 		return FALSE;
251 	return TRUE;
252 }
253 
254 /*
255  * store the session in a self-contained client-side-only cookie storage
256  */
oidc_session_save_cookie(request_rec * r,oidc_session_t * z,apr_byte_t first_time)257 static apr_byte_t oidc_session_save_cookie(request_rec *r, oidc_session_t *z,
258 		apr_byte_t first_time) {
259 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
260 			&auth_openidc_module);
261 	char *cookieValue = "";
262 	if ((z->state != NULL)
263 			&& (oidc_session_encode(r, c, z, &cookieValue, TRUE) == FALSE))
264 		return FALSE;
265 
266 	oidc_util_set_chunked_cookie(r, oidc_cfg_dir_cookie(r), cookieValue,
267 			c->persistent_session_cookie ? z->expiry : -1,
268 					c->session_cookie_chunk_size,
269 					(z->state == NULL) ? OIDC_COOKIE_EXT_SAME_SITE_NONE(r) :
270 							c->cookie_same_site ?
271 									(first_time ?
272 											OIDC_COOKIE_EXT_SAME_SITE_LAX :
273 											OIDC_COOKIE_EXT_SAME_SITE_STRICT) :
274 											OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
275 
276 	return TRUE;
277 }
278 
oidc_session_extract(request_rec * r,oidc_session_t * z)279 apr_byte_t oidc_session_extract(request_rec *r, oidc_session_t *z) {
280 	apr_byte_t rc = FALSE;
281 	const char *ses_p_tb_id = NULL, *env_p_tb_id = NULL;
282 
283 	if (z->state == NULL)
284 		goto out;
285 
286 	json_t *j_expires = json_object_get(z->state, OIDC_SESSION_EXPIRY_KEY);
287 	if (j_expires)
288 		z->expiry = apr_time_from_sec(json_integer_value(j_expires));
289 
290 	/* check whether it has expired */
291 	if (apr_time_now() > z->expiry) {
292 
293 		oidc_warn(r, "session restored from cache has expired");
294 		oidc_session_clear(r, z);
295 
296 		goto out;
297 	}
298 
299 	oidc_session_get(r, z, OIDC_SESSION_PROVIDED_TOKEN_BINDING_KEY,
300 			&ses_p_tb_id);
301 
302 	if (ses_p_tb_id != NULL) {
303 		env_p_tb_id = oidc_util_get_provided_token_binding_id(r);
304 		if ((env_p_tb_id == NULL)
305 				|| (apr_strnatcmp(env_p_tb_id, ses_p_tb_id) != 0)) {
306 			oidc_error(r,
307 					"the Provided Token Binding ID stored in the session doesn't match the one presented by the user agent");
308 			oidc_session_clear(r, z);
309 		}
310 	}
311 
312 	oidc_session_get(r, z, OIDC_SESSION_REMOTE_USER_KEY, &z->remote_user);
313 	oidc_session_get(r, z, OIDC_SESSION_SID_KEY, &z->sid);
314 
315 	rc = TRUE;
316 
317 out:
318 
319 	return rc;
320 }
321 
322 /*
323  * load a session from the cache/cookie
324  */
oidc_session_load(request_rec * r,oidc_session_t ** zz)325 apr_byte_t oidc_session_load(request_rec *r, oidc_session_t **zz) {
326 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
327 			&auth_openidc_module);
328 
329 	apr_byte_t rc = FALSE;
330 
331 	/* allocate space for the session object and fill it */
332 	oidc_session_t *z = (*zz = apr_pcalloc(r->pool, sizeof(oidc_session_t)));
333 	oidc_session_clear(r, z);
334 	z->sid = NULL;
335 
336 	if (c->session_type == OIDC_SESSION_TYPE_SERVER_CACHE)
337 		/* load the session from the cache */
338 		rc = oidc_session_load_cache(r, z);
339 
340 	/* if we get here we configured client-cookie or retrieving from the cache failed */
341 	if ((c->session_type == OIDC_SESSION_TYPE_CLIENT_COOKIE)
342 			|| ((rc == FALSE) && oidc_cfg_session_cache_fallback_to_cookie(r)))
343 		/* load the session from a self-contained cookie */
344 		rc = oidc_session_load_cookie(r, c, z);
345 
346 	if (rc == TRUE)
347 		rc = oidc_session_extract(r, z);
348 
349 	return rc;
350 }
351 
352 /*
353  * save a session to cache/cookie
354  */
oidc_session_save(request_rec * r,oidc_session_t * z,apr_byte_t first_time)355 apr_byte_t oidc_session_save(request_rec *r, oidc_session_t *z,
356 		apr_byte_t first_time) {
357 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
358 			&auth_openidc_module);
359 
360 	apr_byte_t rc = FALSE;
361 	const char *p_tb_id = oidc_util_get_provided_token_binding_id(r);
362 
363 	if (z->state != NULL) {
364 		oidc_session_set(r, z, OIDC_SESSION_REMOTE_USER_KEY, z->remote_user);
365 		json_object_set_new(z->state, OIDC_SESSION_EXPIRY_KEY,
366 				json_integer(apr_time_sec(z->expiry)));
367 
368 		if ((first_time) && (p_tb_id != NULL)) {
369 			oidc_debug(r,
370 					"Provided Token Binding ID environment variable found; adding its value to the session state");
371 			oidc_session_set(r, z, OIDC_SESSION_PROVIDED_TOKEN_BINDING_KEY,
372 					p_tb_id);
373 		}
374 	}
375 
376 	if (c->session_type == OIDC_SESSION_TYPE_SERVER_CACHE)
377 		/* store the session in the cache */
378 		rc = oidc_session_save_cache(r, z, first_time);
379 
380 	/* if we get here we configured client-cookie or saving in the cache failed */
381 	if ((c->session_type == OIDC_SESSION_TYPE_CLIENT_COOKIE)
382 			|| ((rc == FALSE) && oidc_cfg_session_cache_fallback_to_cookie(r)))
383 		/* store the session in a self-contained cookie */
384 		rc = oidc_session_save_cookie(r, z, first_time);
385 
386 	return rc;
387 }
388 
389 /*
390  * free resources allocated for a session
391  */
oidc_session_free(request_rec * r,oidc_session_t * z)392 apr_byte_t oidc_session_free(request_rec *r, oidc_session_t *z) {
393 	oidc_session_clear(r, z);
394 	return TRUE;
395 }
396 
397 /*
398  * terminate a session
399  */
oidc_session_kill(request_rec * r,oidc_session_t * z)400 apr_byte_t oidc_session_kill(request_rec *r, oidc_session_t *z) {
401 	if (z->state) {
402 		json_decref(z->state);
403 		z->state = NULL;
404 	}
405 	oidc_session_save(r, z, FALSE);
406 	return oidc_session_free(r, z);
407 }
408 
409 /*
410  * get a value from the session based on the name from a name/value pair
411  */
oidc_session_get(request_rec * r,oidc_session_t * z,const char * key,const char ** value)412 apr_byte_t oidc_session_get(request_rec *r, oidc_session_t *z, const char *key,
413 		const char **value) {
414 
415 	/* just return the value for the key */
416 	oidc_json_object_get_string(r->pool, z->state, key, (char **) value, NULL);
417 
418 	return TRUE;
419 }
420 
421 /*
422  * set a name/value key pair in the session
423  */
oidc_session_set(request_rec * r,oidc_session_t * z,const char * key,const char * value)424 apr_byte_t oidc_session_set(request_rec *r, oidc_session_t *z, const char *key,
425 		const char *value) {
426 
427 	/* only set it if non-NULL, otherwise delete the entry */
428 	if (value) {
429 		if (z->state == NULL)
430 			z->state = json_object();
431 		json_object_set_new(z->state, key, json_string(value));
432 	} else if (z->state != NULL) {
433 		json_object_del(z->state, key);
434 	}
435 
436 	return TRUE;
437 }
438 
439 /*
440  * session object keys
441  */
442 /* key for storing the userinfo claims in the session context */
443 #define OIDC_SESSION_KEY_USERINFO_CLAIMS "uic"
444 /* key for storing the userinfo JWT in the session context */
445 #define OIDC_SESSION_KEY_USERINFO_JWT "uij"
446 /* key for storing the id_token in the session context */
447 #define OIDC_SESSION_KEY_IDTOKEN_CLAIMS "idc"
448 /* key for storing the raw id_token in the session context */
449 #define OIDC_SESSION_KEY_IDTOKEN "idt"
450 /* key for storing the access_token in the session context */
451 #define OIDC_SESSION_KEY_ACCESSTOKEN "at"
452 /* key for storing the access_token expiry in the session context */
453 #define OIDC_SESSION_KEY_ACCESSTOKEN_EXPIRES "ate"
454 /* key for storing the refresh_token in the session context */
455 #define OIDC_SESSION_KEY_REFRESH_TOKEN "rt"
456 /* key for storing maximum session duration in the session context */
457 #define OIDC_SESSION_KEY_SESSION_EXPIRES "se"
458 /* key for storing the cookie domain in the session context */
459 #define OIDC_SESSION_KEY_COOKIE_DOMAIN "cd"
460 /* key for storing last user info refresh timestamp in the session context */
461 #define OIDC_SESSION_KEY_USERINFO_LAST_REFRESH "uilr"
462 /* key for storing last access token refresh timestamp in the session context */
463 #define OIDC_SESSION_KEY_ACCESS_TOKEN_LAST_REFRESH "atlr"
464 /* key for storing request state */
465 #define OIDC_SESSION_KEY_REQUEST_STATE "rs"
466 /* key for storing the original URL */
467 #define OIDC_SESSION_KEY_ORIGINAL_URL "ou"
468 /* key for storing the session_state in the session context */
469 #define OIDC_SESSION_KEY_SESSION_STATE "ss"
470 /* key for storing the issuer in the session context */
471 #define OIDC_SESSION_KEY_ISSUER "iss"
472 
473 /*
474  * helper functions
475  */
476 typedef const char *(*oidc_session_get_str_function)(request_rec *r,
477 		oidc_session_t *z);
478 
oidc_session_set_timestamp(request_rec * r,oidc_session_t * z,const char * key,const apr_time_t timestamp)479 static void oidc_session_set_timestamp(request_rec *r, oidc_session_t *z,
480 		const char *key, const apr_time_t timestamp) {
481 	if (timestamp != -1)
482 		oidc_session_set(r, z, key,
483 				apr_psprintf(r->pool, "%" APR_TIME_T_FMT, timestamp));
484 }
485 
oidc_session_get_str2json(request_rec * r,oidc_session_t * z,oidc_session_get_str_function session_get_str_fn)486 static json_t *oidc_session_get_str2json(request_rec *r, oidc_session_t *z,
487 		oidc_session_get_str_function session_get_str_fn) {
488 	json_t *json = NULL;
489 	const char *str = session_get_str_fn(r, z);
490 	if (str != NULL)
491 		oidc_util_decode_json_object(r, str, &json);
492 	return json;
493 }
494 
oidc_session_get_key2string(request_rec * r,oidc_session_t * z,const char * key)495 static const char *oidc_session_get_key2string(request_rec *r,
496 		oidc_session_t *z, const char *key) {
497 	const char *s_value = NULL;
498 	oidc_session_get(r, z, key, &s_value);
499 	return s_value;
500 }
501 
oidc_session_get_key2timestamp(request_rec * r,oidc_session_t * z,const char * key)502 static apr_time_t oidc_session_get_key2timestamp(request_rec *r,
503 		oidc_session_t *z, const char *key) {
504 	apr_time_t t_expires = 0;
505 	const char *s_expires = oidc_session_get_key2string(r, z, key);
506 	if (s_expires != NULL)
507 		sscanf(s_expires, "%" APR_TIME_T_FMT, &t_expires);
508 	return t_expires;
509 }
510 
oidc_session_set_filtered_claims(request_rec * r,oidc_session_t * z,const char * session_key,const char * claims)511 void oidc_session_set_filtered_claims(request_rec *r, oidc_session_t *z,
512 		const char *session_key, const char *claims) {
513 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
514 			&auth_openidc_module);
515 
516 	const char *name;
517 	json_t *src = NULL, *dst = NULL, *value = NULL;
518 	void *iter = NULL;
519 	apr_byte_t is_allowed;
520 
521 	if (oidc_util_decode_json_object(r, claims, &src) == FALSE) {
522 		oidc_session_set(r, z, session_key, NULL);
523 		return;
524 	}
525 
526 	dst = json_object();
527 	iter = json_object_iter(src);
528 	while (iter) {
529 		is_allowed = TRUE;
530 		name = json_object_iter_key(iter);
531 		value = json_object_iter_value(iter);
532 
533 		if ((c->black_listed_claims != NULL)
534 				&& (apr_hash_get(c->black_listed_claims, name,
535 						APR_HASH_KEY_STRING) != NULL)) {
536 			oidc_debug(r, "removing blacklisted claim [%s]: '%s'", session_key,
537 					name);
538 			is_allowed = FALSE;
539 		}
540 
541 		if ((is_allowed == TRUE) && (c->white_listed_claims != NULL)
542 				&& (apr_hash_get(c->white_listed_claims, name,
543 						APR_HASH_KEY_STRING) == NULL)) {
544 			oidc_debug(r, "removing non-whitelisted claim [%s]: '%s'",
545 					session_key, name);
546 			is_allowed = FALSE;
547 		}
548 
549 		if (is_allowed == TRUE)
550 			json_object_set(dst, name, value);
551 
552 		iter = json_object_iter_next(src, iter);
553 	}
554 
555 	char *filtered_claims = oidc_util_encode_json_object(r, dst, JSON_COMPACT);
556 	json_decref(dst);
557 	json_decref(src);
558 	oidc_session_set(r, z, session_key, filtered_claims);
559 }
560 
561 /*
562  * userinfo claims
563  */
oidc_session_set_userinfo_claims(request_rec * r,oidc_session_t * z,const char * claims)564 void oidc_session_set_userinfo_claims(request_rec *r, oidc_session_t *z,
565 		const char *claims) {
566 	oidc_session_set_filtered_claims(r, z, OIDC_SESSION_KEY_USERINFO_CLAIMS,
567 			claims);
568 }
569 
oidc_session_get_userinfo_claims(request_rec * r,oidc_session_t * z)570 const char * oidc_session_get_userinfo_claims(request_rec *r, oidc_session_t *z) {
571 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_USERINFO_CLAIMS);
572 }
573 
oidc_session_get_userinfo_claims_json(request_rec * r,oidc_session_t * z)574 json_t *oidc_session_get_userinfo_claims_json(request_rec *r, oidc_session_t *z) {
575 	return oidc_session_get_str2json(r, z, oidc_session_get_userinfo_claims);
576 }
577 
oidc_session_set_userinfo_jwt(request_rec * r,oidc_session_t * z,const char * s_userinfo_jwt)578 void oidc_session_set_userinfo_jwt(request_rec *r, oidc_session_t *z,
579 		const char *s_userinfo_jwt) {
580 	oidc_session_set(r, z, OIDC_SESSION_KEY_USERINFO_JWT, s_userinfo_jwt);
581 }
582 
oidc_session_get_userinfo_jwt(request_rec * r,oidc_session_t * z)583 const char * oidc_session_get_userinfo_jwt(request_rec *r, oidc_session_t *z) {
584 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_USERINFO_JWT);
585 }
586 
587 /*
588  * id_token claims
589  */
oidc_session_set_idtoken_claims(request_rec * r,oidc_session_t * z,const char * idtoken_claims)590 void oidc_session_set_idtoken_claims(request_rec *r, oidc_session_t *z,
591 		const char *idtoken_claims) {
592 	oidc_session_set_filtered_claims(r, z, OIDC_SESSION_KEY_IDTOKEN_CLAIMS,
593 			idtoken_claims);
594 }
595 
oidc_session_get_idtoken_claims(request_rec * r,oidc_session_t * z)596 const char * oidc_session_get_idtoken_claims(request_rec *r, oidc_session_t *z) {
597 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_IDTOKEN_CLAIMS);
598 }
599 
oidc_session_get_idtoken_claims_json(request_rec * r,oidc_session_t * z)600 json_t *oidc_session_get_idtoken_claims_json(request_rec *r, oidc_session_t *z) {
601 	return oidc_session_get_str2json(r, z, oidc_session_get_idtoken_claims);
602 }
603 
604 /*
605  * compact serialized id_token
606  */
oidc_session_set_idtoken(request_rec * r,oidc_session_t * z,const char * s_id_token)607 void oidc_session_set_idtoken(request_rec *r, oidc_session_t *z,
608 		const char *s_id_token) {
609 	oidc_session_set(r, z, OIDC_SESSION_KEY_IDTOKEN, s_id_token);
610 }
611 
oidc_session_get_idtoken(request_rec * r,oidc_session_t * z)612 const char * oidc_session_get_idtoken(request_rec *r, oidc_session_t *z) {
613 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_IDTOKEN);
614 }
615 
616 /*
617  * access token
618  */
oidc_session_set_access_token(request_rec * r,oidc_session_t * z,const char * access_token)619 void oidc_session_set_access_token(request_rec *r, oidc_session_t *z,
620 		const char *access_token) {
621 	oidc_session_set(r, z, OIDC_SESSION_KEY_ACCESSTOKEN, access_token);
622 }
623 
oidc_session_get_access_token(request_rec * r,oidc_session_t * z)624 const char * oidc_session_get_access_token(request_rec *r, oidc_session_t *z) {
625 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ACCESSTOKEN);
626 }
627 
628 /*
629  * access token expires
630  */
oidc_session_set_access_token_expires(request_rec * r,oidc_session_t * z,const int expires_in)631 void oidc_session_set_access_token_expires(request_rec *r, oidc_session_t *z,
632 		const int expires_in) {
633 	if (expires_in != -1) {
634 		oidc_session_set(r, z, OIDC_SESSION_KEY_ACCESSTOKEN_EXPIRES,
635 				apr_psprintf(r->pool, "%" APR_TIME_T_FMT,
636 						apr_time_sec(apr_time_now()) + expires_in));
637 	}
638 }
639 
oidc_session_get_access_token_expires(request_rec * r,oidc_session_t * z)640 const char * oidc_session_get_access_token_expires(request_rec *r,
641 		oidc_session_t *z) {
642 	return oidc_session_get_key2string(r, z,
643 			OIDC_SESSION_KEY_ACCESSTOKEN_EXPIRES);
644 }
645 
646 /*
647  * refresh token
648  */
oidc_session_set_refresh_token(request_rec * r,oidc_session_t * z,const char * refresh_token)649 void oidc_session_set_refresh_token(request_rec *r, oidc_session_t *z,
650 		const char *refresh_token) {
651 	oidc_session_set(r, z, OIDC_SESSION_KEY_REFRESH_TOKEN, refresh_token);
652 }
653 
oidc_session_get_refresh_token(request_rec * r,oidc_session_t * z)654 const char * oidc_session_get_refresh_token(request_rec *r, oidc_session_t *z) {
655 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_REFRESH_TOKEN);
656 }
657 
658 /*
659  * session expires
660  */
oidc_session_set_session_expires(request_rec * r,oidc_session_t * z,const apr_time_t expires)661 void oidc_session_set_session_expires(request_rec *r, oidc_session_t *z,
662 		const apr_time_t expires) {
663 	oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_SESSION_EXPIRES, expires);
664 }
665 
oidc_session_get_session_expires(request_rec * r,oidc_session_t * z)666 apr_time_t oidc_session_get_session_expires(request_rec *r, oidc_session_t *z) {
667 	return oidc_session_get_key2timestamp(r, z,
668 			OIDC_SESSION_KEY_SESSION_EXPIRES);
669 }
670 
671 /*
672  * cookie domain
673  */
oidc_session_set_cookie_domain(request_rec * r,oidc_session_t * z,const char * cookie_domain)674 void oidc_session_set_cookie_domain(request_rec *r, oidc_session_t *z,
675 		const char *cookie_domain) {
676 	oidc_session_set(r, z, OIDC_SESSION_KEY_COOKIE_DOMAIN, cookie_domain);
677 }
678 
oidc_session_get_cookie_domain(request_rec * r,oidc_session_t * z)679 const char * oidc_session_get_cookie_domain(request_rec *r, oidc_session_t *z) {
680 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_COOKIE_DOMAIN);
681 }
682 
683 /*
684  * userinfo last refresh
685  */
oidc_session_reset_userinfo_last_refresh(request_rec * r,oidc_session_t * z)686 void oidc_session_reset_userinfo_last_refresh(request_rec *r, oidc_session_t *z) {
687 	oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_USERINFO_LAST_REFRESH,
688 			apr_time_now());
689 }
690 
oidc_session_get_userinfo_last_refresh(request_rec * r,oidc_session_t * z)691 apr_time_t oidc_session_get_userinfo_last_refresh(request_rec *r,
692 		oidc_session_t *z) {
693 	return oidc_session_get_key2timestamp(r, z,
694 			OIDC_SESSION_KEY_USERINFO_LAST_REFRESH);
695 }
696 
697 /*
698  * access_token last refresh
699  */
oidc_session_reset_access_token_last_refresh(request_rec * r,oidc_session_t * z)700 void oidc_session_reset_access_token_last_refresh(request_rec *r,
701 		oidc_session_t *z) {
702 	oidc_session_set_timestamp(r, z, OIDC_SESSION_KEY_ACCESS_TOKEN_LAST_REFRESH,
703 			apr_time_now());
704 }
705 
oidc_session_get_access_token_last_refresh(request_rec * r,oidc_session_t * z)706 apr_time_t oidc_session_get_access_token_last_refresh(request_rec *r,
707 		oidc_session_t *z) {
708 	return oidc_session_get_key2timestamp(r, z,
709 			OIDC_SESSION_KEY_ACCESS_TOKEN_LAST_REFRESH);
710 }
711 
712 /*
713  * request state
714  */
oidc_session_set_request_state(request_rec * r,oidc_session_t * z,const char * request_state)715 void oidc_session_set_request_state(request_rec *r, oidc_session_t *z,
716 		const char *request_state) {
717 	oidc_session_set(r, z, OIDC_SESSION_KEY_REQUEST_STATE, request_state);
718 }
719 
oidc_session_get_request_state(request_rec * r,oidc_session_t * z)720 const char * oidc_session_get_request_state(request_rec *r, oidc_session_t *z) {
721 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_REQUEST_STATE);
722 }
723 
724 /*
725  * original url
726  */
oidc_session_set_original_url(request_rec * r,oidc_session_t * z,const char * original_url)727 void oidc_session_set_original_url(request_rec *r, oidc_session_t *z,
728 		const char *original_url) {
729 	oidc_session_set(r, z, OIDC_SESSION_KEY_ORIGINAL_URL, original_url);
730 }
731 
oidc_session_get_original_url(request_rec * r,oidc_session_t * z)732 const char * oidc_session_get_original_url(request_rec *r, oidc_session_t *z) {
733 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ORIGINAL_URL);
734 }
735 
736 /*
737  * session state
738  */
oidc_session_set_session_state(request_rec * r,oidc_session_t * z,const char * session_state)739 void oidc_session_set_session_state(request_rec *r, oidc_session_t *z,
740 		const char *session_state) {
741 	oidc_session_set(r, z, OIDC_SESSION_KEY_SESSION_STATE, session_state);
742 }
743 
oidc_session_get_session_state(request_rec * r,oidc_session_t * z)744 const char * oidc_session_get_session_state(request_rec *r, oidc_session_t *z) {
745 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_SESSION_STATE);
746 }
747 
748 /*
749  * issuer
750  */
oidc_session_set_issuer(request_rec * r,oidc_session_t * z,const char * issuer)751 void oidc_session_set_issuer(request_rec *r, oidc_session_t *z,
752 		const char *issuer) {
753 	oidc_session_set(r, z, OIDC_SESSION_KEY_ISSUER, issuer);
754 }
755 
oidc_session_get_issuer(request_rec * r,oidc_session_t * z)756 const char * oidc_session_get_issuer(request_rec *r, oidc_session_t *z) {
757 	return oidc_session_get_key2string(r, z, OIDC_SESSION_KEY_ISSUER);
758 }
759