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 * Validation and parsing of configuration values.
42 *
43 * @Author: Hans Zandbelt - hans.zandbelt@zmartzone.eu
44 */
45
46 #include <apr_base64.h>
47 #include "mod_auth_openidc.h"
48 #include "parse.h"
49 #include "jose.h"
50
51 /*
52 * parse a URL according to one of two schemes (NULL for any)
53 */
oidc_valid_url_scheme(apr_pool_t * pool,const char * arg,const char * scheme1,const char * scheme2)54 static const char * oidc_valid_url_scheme(apr_pool_t *pool, const char *arg,
55 const char *scheme1, const char *scheme2) {
56
57 apr_uri_t uri;
58
59 if (apr_uri_parse(pool, arg, &uri) != APR_SUCCESS) {
60 return apr_psprintf(pool, "'%s' cannot be parsed as a URL", arg);
61 }
62
63 if (uri.scheme == NULL) {
64 return apr_psprintf(pool,
65 "'%s' cannot be parsed as a URL (no scheme set)", arg);
66 }
67
68 if ((scheme1 != NULL) && (apr_strnatcmp(uri.scheme, scheme1) != 0)) {
69 if ((scheme2 != NULL) && (apr_strnatcmp(uri.scheme, scheme2) != 0)) {
70 return apr_psprintf(pool,
71 "'%s' cannot be parsed as a \"%s\" or \"%s\" URL (scheme == %s)!",
72 arg, scheme1, scheme2, uri.scheme);
73 } else if (scheme2 == NULL) {
74 return apr_psprintf(pool,
75 "'%s' cannot be parsed as a \"%s\" URL (scheme == %s)!",
76 arg, scheme1, uri.scheme);
77 }
78 }
79
80 if (uri.hostname == NULL) {
81 return apr_psprintf(pool,
82 "'%s' cannot be parsed as a valid URL (no hostname set, check your slashes)",
83 arg);
84 }
85
86 return NULL;
87 }
88
89 /*
90 * parse a URL according to a scheme
91 */
oidc_valid_url(apr_pool_t * pool,const char * arg,const char * scheme)92 const char *oidc_valid_url(apr_pool_t *pool, const char *arg,
93 const char *scheme) {
94 return oidc_valid_url_scheme(pool, arg, scheme, NULL);
95 }
96
97 /*
98 * parse a URL that should conform to any HTTP scheme (http/https)
99 */
oidc_valid_http_url(apr_pool_t * pool,const char * arg)100 const char *oidc_valid_http_url(apr_pool_t *pool, const char *arg) {
101 return oidc_valid_url_scheme(pool, arg, "https", "http");
102 }
103
104 #define STR_ERROR_MAX 128
105
106 /*
107 * check if arg is a valid directory on the file system
108 */
oidc_valid_dir(apr_pool_t * pool,const char * arg)109 const char *oidc_valid_dir(apr_pool_t *pool, const char *arg) {
110 char s_err[STR_ERROR_MAX];
111 apr_dir_t *dir = NULL;
112 apr_status_t rc = APR_SUCCESS;
113
114 /* ensure the directory exists */
115 if ((rc = apr_dir_open(&dir, arg, pool)) != APR_SUCCESS) {
116 return apr_psprintf(pool, "cannot access directory '%s' (%s)", arg,
117 apr_strerror(rc, s_err, STR_ERROR_MAX));
118 }
119
120 /* and cleanup... */
121 if ((rc = apr_dir_close(dir)) != APR_SUCCESS) {
122 return apr_psprintf(pool, "cannot close directory '%s' (%s)", arg,
123 apr_strerror(rc, s_err, STR_ERROR_MAX));
124 }
125
126 return NULL;
127 }
128
129 /*
130 * check if arg is a valid cookie domain value
131 */
oidc_valid_cookie_domain(apr_pool_t * pool,const char * arg)132 const char *oidc_valid_cookie_domain(apr_pool_t *pool, const char *arg) {
133 size_t sz, limit;
134 char d;
135 limit = strlen(arg);
136 for (sz = 0; sz < limit; sz++) {
137 d = arg[sz];
138 if ((d < '0' || d > '9') && (d < 'a' || d > 'z') && (d < 'A' || d > 'Z')
139 && d != '.' && d != '-') {
140 return (apr_psprintf(pool,
141 "invalid character '%c' in cookie domain value: %s", d, arg));
142 }
143 }
144 return NULL;
145 }
146
147 /*
148 * parse an integer value from a string
149 */
oidc_parse_int(apr_pool_t * pool,const char * arg,int * int_value)150 const char *oidc_parse_int(apr_pool_t *pool, const char *arg, int *int_value) {
151 char *endptr;
152 int v = strtol(arg, &endptr, 10);
153 if ((*arg == '\0') || (*endptr != '\0')) {
154 return apr_psprintf(pool, "invalid integer value: %s", arg);
155 }
156 *int_value = v;
157 return NULL;
158 }
159
160 /*
161 * check if the provided integer value is between a specified minimum and maximum
162 */
oidc_valid_int_min_max(apr_pool_t * pool,int value,int min_value,int max_value)163 static const char *oidc_valid_int_min_max(apr_pool_t *pool, int value,
164 int min_value, int max_value) {
165 if (value < min_value) {
166 return apr_psprintf(pool,
167 "integer value %d is smaller than the minimum allowed value %d",
168 value, min_value);
169 }
170 if (value > max_value) {
171 return apr_psprintf(pool,
172 "integer value %d is greater than the maximum allowed value %d",
173 value, max_value);
174 }
175 return NULL;
176 }
177
178 /*
179 * parse an integer and check validity
180 */
oidc_parse_int_valid(apr_pool_t * pool,const char * arg,int * int_value,oidc_valid_int_function_t valid_int_function)181 static const char *oidc_parse_int_valid(apr_pool_t *pool, const char *arg,
182 int *int_value, oidc_valid_int_function_t valid_int_function) {
183 int v = 0;
184 const char *rv = NULL;
185 rv = oidc_parse_int(pool, arg, &v);
186 if (rv != NULL)
187 return rv;
188 rv = valid_int_function(pool, v);
189 if (rv != NULL)
190 return rv;
191 *int_value = v;
192 return NULL;
193 }
194
195 /*
196 * parse an integer value from a string that must be between a specified minimum and maximum
197 */
oidc_parse_int_min_max(apr_pool_t * pool,const char * arg,int * int_value,int min_value,int max_value)198 static const char *oidc_parse_int_min_max(apr_pool_t *pool, const char *arg,
199 int *int_value, int min_value, int max_value) {
200 int v = 0;
201 const char *rv = NULL;
202 rv = oidc_parse_int(pool, arg, &v);
203 if (rv != NULL)
204 return rv;
205 rv = oidc_valid_int_min_max(pool, v, min_value, max_value);
206 if (rv != NULL)
207 return rv;
208 *int_value = v;
209 return NULL;
210 }
211
212 #define OIDC_LIST_OPTIONS_START "["
213 #define OIDC_LIST_OPTIONS_END "]"
214 #define OIDC_LIST_OPTIONS_SEPARATOR "|"
215 #define OIDC_LIST_OPTIONS_QUOTE "'"
216
217 /*
218 * flatten the list of string options, separated by the specified separator char
219 */
oidc_flatten_list_options(apr_pool_t * pool,char * options[])220 static char *oidc_flatten_list_options(apr_pool_t *pool, char *options[]) {
221 int i = 0;
222 char *result = OIDC_LIST_OPTIONS_START;
223 while (options[i] != NULL) {
224 if (i == 0)
225 result = apr_psprintf(pool, "%s%s%s%s", OIDC_LIST_OPTIONS_START,
226 OIDC_LIST_OPTIONS_QUOTE, options[i],
227 OIDC_LIST_OPTIONS_QUOTE);
228 else
229 result = apr_psprintf(pool, "%s%s%s%s%s", result,
230 OIDC_LIST_OPTIONS_SEPARATOR, OIDC_LIST_OPTIONS_QUOTE, options[i],
231 OIDC_LIST_OPTIONS_QUOTE);
232 i++;
233 }
234 result = apr_psprintf(pool, "%s%s", result, OIDC_LIST_OPTIONS_END);
235 return result;
236 }
237
238 /*
239 * check if arg is a valid option in the list of provided string options
240 */
oidc_valid_string_option(apr_pool_t * pool,const char * arg,char * options[])241 static const char *oidc_valid_string_option(apr_pool_t *pool, const char *arg,
242 char *options[]) {
243 int i = 0;
244 while (options[i] != NULL) {
245 if (apr_strnatcmp(arg, options[i]) == 0)
246 break;
247 i++;
248 }
249 if (options[i] == NULL) {
250 return apr_psprintf(pool, "invalid value %s%s%s, must be one of %s",
251 OIDC_LIST_OPTIONS_QUOTE, arg, OIDC_LIST_OPTIONS_QUOTE,
252 oidc_flatten_list_options(pool, options));
253 }
254 return NULL;
255 }
256
257 #define OIDC_CACHE_TYPE_SHM "shm"
258 #define OIDC_CACHE_TYPE_MEMCACHE "memcache"
259 #define OIDC_CACHE_TYPE_REDIS "redis"
260 #define OIDC_CACHE_TYPE_FILE "file"
261
262 /*
263 * parse the cache backend type
264 */
oidc_parse_cache_type(apr_pool_t * pool,const char * arg,oidc_cache_t ** type)265 const char *oidc_parse_cache_type(apr_pool_t *pool, const char *arg,
266 oidc_cache_t **type) {
267 static char *options[] = {
268 OIDC_CACHE_TYPE_SHM,
269 #ifdef USE_MEMCACHE
270 OIDC_CACHE_TYPE_MEMCACHE,
271 #endif
272 #ifdef USE_LIBHIREDIS
273 OIDC_CACHE_TYPE_REDIS,
274 #endif
275 OIDC_CACHE_TYPE_FILE,
276 NULL };
277 const char *rv = oidc_valid_string_option(pool, arg, options);
278 if (rv != NULL)
279 return rv;
280
281 if (apr_strnatcmp(arg, OIDC_CACHE_TYPE_SHM) == 0) {
282 *type = &oidc_cache_shm;
283 #ifdef USE_MEMCACHE
284 } else if (apr_strnatcmp(arg, OIDC_CACHE_TYPE_MEMCACHE) == 0) {
285 *type = &oidc_cache_memcache;
286 #endif
287 } else if (apr_strnatcmp(arg, OIDC_CACHE_TYPE_FILE) == 0) {
288 *type = &oidc_cache_file;
289 #ifdef USE_LIBHIREDIS
290 } else if (apr_strnatcmp(arg, OIDC_CACHE_TYPE_REDIS) == 0) {
291 *type = &oidc_cache_redis;
292 #endif
293 }
294
295 return NULL;
296 }
297
298 #define OIDC_SESSION_TYPE_SERVER_CACHE_STR "server-cache"
299 #define OIDC_SESSION_TYPE_CLIENT_COOKIE_STR "client-cookie"
300 #define OIDC_SESSION_TYPE_PERSISTENT "persistent"
301 #define OIDC_SESSION_TYPE_SEPARATOR ":"
302
303 /*
304 * parse the session mechanism type and the cookie persistency property
305 */
oidc_parse_session_type(apr_pool_t * pool,const char * arg,int * type,int * persistent)306 const char *oidc_parse_session_type(apr_pool_t *pool, const char *arg,
307 int *type, int *persistent) {
308 static char *options[] =
309 {
310 OIDC_SESSION_TYPE_SERVER_CACHE_STR,
311 OIDC_SESSION_TYPE_SERVER_CACHE_STR OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_PERSISTENT,
312 OIDC_SESSION_TYPE_CLIENT_COOKIE_STR,
313 OIDC_SESSION_TYPE_CLIENT_COOKIE_STR OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_PERSISTENT,
314 NULL };
315 const char *rv = oidc_valid_string_option(pool, arg, options);
316 if (rv != NULL)
317 return rv;
318
319 char *s = apr_pstrdup(pool, arg);
320 char *p = strstr(s, OIDC_SESSION_TYPE_SEPARATOR);
321
322 if (p) {
323 *persistent = 1;
324 *p = '\0';
325 }
326
327 if (apr_strnatcmp(s, OIDC_SESSION_TYPE_SERVER_CACHE_STR) == 0) {
328 *type = OIDC_SESSION_TYPE_SERVER_CACHE;
329 } else if (apr_strnatcmp(s, OIDC_SESSION_TYPE_CLIENT_COOKIE_STR) == 0) {
330 *type = OIDC_SESSION_TYPE_CLIENT_COOKIE;
331 }
332 return NULL;
333 }
334
335 /* minimum size of a SHM cache entry */
336 #define OIDC_MINIMUM_CACHE_SHM_ENTRY_SIZE_MAX 8192 + 512 + 17 // 8Kb plus overhead
337 /* maximum size of a SHM cache entry */
338 #define OIDC_MAXIMUM_CACHE_SHM_ENTRY_SIZE_MAX 1024 * 512 // 512Kb incl. overhead
339
340 /*
341 * parse the slot size of a SHM cache entry
342 */
oidc_parse_cache_shm_entry_size_max(apr_pool_t * pool,const char * arg,int * int_value)343 const char *oidc_parse_cache_shm_entry_size_max(apr_pool_t *pool,
344 const char *arg, int *int_value) {
345 return oidc_parse_int_min_max(pool, arg, int_value,
346 OIDC_MINIMUM_CACHE_SHM_ENTRY_SIZE_MAX,
347 OIDC_MAXIMUM_CACHE_SHM_ENTRY_SIZE_MAX);
348 }
349
350 /*
351 * parse a boolean value from a provided string
352 */
oidc_parse_boolean(apr_pool_t * pool,const char * arg,int * bool_value)353 const char *oidc_parse_boolean(apr_pool_t *pool, const char *arg,
354 int *bool_value) {
355 if ((apr_strnatcasecmp(arg, "true") == 0)
356 || (apr_strnatcasecmp(arg, "on") == 0)
357 || (apr_strnatcasecmp(arg, "yes") == 0)
358 || (apr_strnatcasecmp(arg, "1") == 0)) {
359 *bool_value = TRUE;
360 return NULL;
361 }
362 if ((apr_strnatcasecmp(arg, "false") == 0)
363 || (apr_strnatcasecmp(arg, "off") == 0)
364 || (apr_strnatcasecmp(arg, "no") == 0)
365 || (apr_strnatcasecmp(arg, "0") == 0)) {
366 *bool_value = FALSE;
367 return NULL;
368 }
369 return apr_psprintf(pool,
370 "oidc_parse_boolean: could not parse boolean value from \"%s\"",
371 arg);
372 }
373
374 #define OIDC_ENDPOINT_AUTH_CLIENT_SECRET_POST "client_secret_post"
375 #define OIDC_ENDPOINT_AUTH_CLIENT_SECRET_JWT "client_secret_jwt"
376 #define OIDC_ENDPOINT_AUTH_PRIVATE_KEY_JWT "private_key_jwt"
377 #define OIDC_ENDPOINT_AUTH_BEARER_ACCESS_TOKEN "bearer_access_token"
378 #define OIDC_ENDPOINT_AUTH_NONE "none"
379
380 /*
381 * check if the provided endpoint authentication method is supported
382 */
oidc_valid_endpoint_auth_method_impl(apr_pool_t * pool,const char * arg,apr_byte_t has_private_key)383 static const char *oidc_valid_endpoint_auth_method_impl(apr_pool_t *pool,
384 const char *arg, apr_byte_t has_private_key) {
385 static char *options[] = {
386 OIDC_ENDPOINT_AUTH_CLIENT_SECRET_POST,
387 OIDC_ENDPOINT_AUTH_CLIENT_SECRET_BASIC,
388 OIDC_ENDPOINT_AUTH_CLIENT_SECRET_JWT,
389 OIDC_ENDPOINT_AUTH_NONE,
390 OIDC_ENDPOINT_AUTH_BEARER_ACCESS_TOKEN,
391 NULL,
392 NULL };
393 if (has_private_key)
394 options[5] = OIDC_ENDPOINT_AUTH_PRIVATE_KEY_JWT;
395
396 return oidc_valid_string_option(pool, arg, options);
397 }
398
oidc_valid_endpoint_auth_method(apr_pool_t * pool,const char * arg)399 const char *oidc_valid_endpoint_auth_method(apr_pool_t *pool, const char *arg) {
400 return oidc_valid_endpoint_auth_method_impl(pool, arg, TRUE);
401 }
402
oidc_valid_endpoint_auth_method_no_private_key(apr_pool_t * pool,const char * arg)403 const char *oidc_valid_endpoint_auth_method_no_private_key(apr_pool_t *pool,
404 const char *arg) {
405 return oidc_valid_endpoint_auth_method_impl(pool, arg, FALSE);
406 }
407
408 /*
409 * check if the provided OAuth/OIDC response type is supported
410 */
oidc_valid_response_type(apr_pool_t * pool,const char * arg)411 const char *oidc_valid_response_type(apr_pool_t *pool, const char *arg) {
412 if (oidc_proto_flow_is_supported(pool, arg) == FALSE) {
413 return apr_psprintf(pool,
414 "oidc_valid_response_type: type must be one of %s",
415 apr_array_pstrcat(pool, oidc_proto_supported_flows(pool),
416 OIDC_CHAR_PIPE));
417 }
418 return NULL;
419 }
420
421 /*
422 * check if the provided PKCE method is supported
423 */
oidc_valid_pkce_method(apr_pool_t * pool,const char * arg)424 const char *oidc_valid_pkce_method(apr_pool_t *pool, const char *arg) {
425 static char *options[] = {
426 OIDC_PKCE_METHOD_PLAIN,
427 OIDC_PKCE_METHOD_S256,
428 OIDC_PKCE_METHOD_REFERRED_TB,
429 NULL };
430 return oidc_valid_string_option(pool, arg, options);
431 }
432
433 #define OIDC_RESPONSE_TYPE_FRAGMENT "fragment"
434 #define OIDC_RESPONSE_TYPE_QUERY "query"
435 #define OIDC_RESPONSE_TYPE_FORM_POST "form_post"
436
437 /*
438 * check if the provided OAuth 2.0 response mode is supported
439 */
oidc_valid_response_mode(apr_pool_t * pool,const char * arg)440 const char *oidc_valid_response_mode(apr_pool_t *pool, const char *arg) {
441 static char *options[] = {
442 OIDC_RESPONSE_TYPE_FRAGMENT,
443 OIDC_RESPONSE_TYPE_QUERY,
444 OIDC_RESPONSE_TYPE_FORM_POST,
445 NULL };
446 return oidc_valid_string_option(pool, arg, options);
447 }
448
449 /*
450 * check if the provided JWT signature algorithm is supported
451 */
oidc_valid_signed_response_alg(apr_pool_t * pool,const char * arg)452 const char *oidc_valid_signed_response_alg(apr_pool_t *pool, const char *arg) {
453 if (oidc_jose_jws_algorithm_is_supported(pool, arg) == FALSE) {
454 return apr_psprintf(pool,
455 "unsupported/invalid signing algorithm '%s'; must be one of [%s]",
456 arg,
457 apr_array_pstrcat(pool,
458 oidc_jose_jws_supported_algorithms(pool),
459 OIDC_CHAR_PIPE));
460 }
461 return NULL;
462 }
463
464 /*
465 * check if the provided JWT content key encryption algorithm is supported
466 */
oidc_valid_encrypted_response_alg(apr_pool_t * pool,const char * arg)467 const char *oidc_valid_encrypted_response_alg(apr_pool_t *pool, const char *arg) {
468 if (oidc_jose_jwe_algorithm_is_supported(pool, arg) == FALSE) {
469 return apr_psprintf(pool,
470 "unsupported/invalid encryption algorithm '%s'; must be one of [%s]",
471 arg,
472 apr_array_pstrcat(pool,
473 oidc_jose_jwe_supported_algorithms(pool),
474 OIDC_CHAR_PIPE));
475 }
476 return NULL;
477 }
478
479 /*
480 * check if the provided JWT encryption cipher is supported
481 */
oidc_valid_encrypted_response_enc(apr_pool_t * pool,const char * arg)482 const char *oidc_valid_encrypted_response_enc(apr_pool_t *pool, const char *arg) {
483 if (oidc_jose_jwe_encryption_is_supported(pool, arg) == FALSE) {
484 return apr_psprintf(pool,
485 "unsupported/invalid encryption type '%s'; must be one of [%s]",
486 arg,
487 apr_array_pstrcat(pool,
488 oidc_jose_jwe_supported_encryptions(pool),
489 OIDC_CHAR_PIPE));
490 }
491 return NULL;
492 }
493
494 #define OIDC_SESSION_INACTIVITY_TIMEOUT_MIN 10
495 #define OIDC_SESSION_INACTIVITY_TIMEOUT_MAX 3600 * 24 * 365
496
497 /*
498 * parse a session inactivity timeout value from the provided string
499 */
oidc_parse_session_inactivity_timeout(apr_pool_t * pool,const char * arg,int * int_value)500 const char *oidc_parse_session_inactivity_timeout(apr_pool_t *pool,
501 const char *arg, int *int_value) {
502 return oidc_parse_int_min_max(pool, arg, int_value,
503 OIDC_SESSION_INACTIVITY_TIMEOUT_MIN,
504 OIDC_SESSION_INACTIVITY_TIMEOUT_MAX);
505 }
506
507 #define OIDC_SESSION_MAX_DURATION_MIN 15
508 #define OIDC_SESSION_MAX_DURATION_MAX 3600 * 24 * 365
509
510 /*
511 * check the boundaries for session max lifetime
512 */
oidc_valid_session_max_duration(apr_pool_t * pool,int v)513 const char *oidc_valid_session_max_duration(apr_pool_t *pool, int v) {
514 if (v == 0) {
515 return NULL;
516 }
517 if (v < OIDC_SESSION_MAX_DURATION_MIN) {
518 return apr_psprintf(pool, "value must not be less than %d seconds",
519 OIDC_SESSION_MAX_DURATION_MIN);
520 }
521 if (v > OIDC_SESSION_MAX_DURATION_MAX) {
522 return apr_psprintf(pool, "value must not be greater than %d seconds",
523 OIDC_SESSION_MAX_DURATION_MAX);
524 }
525 return NULL;
526 }
527
528 #define OIDC_MAX_NUMBER_OF_STATE_COOKIES_MIN 0
529 #define OIDC_MAX_NUMBER_OF_STATE_COOKIES_MAX 255
530
531 /*
532 * check the maximum number of parallel state cookies
533 */
oidc_valid_max_number_of_state_cookies(apr_pool_t * pool,int v)534 const char *oidc_valid_max_number_of_state_cookies(apr_pool_t *pool, int v) {
535 if (v == 0) {
536 return NULL;
537 }
538 if (v < OIDC_MAX_NUMBER_OF_STATE_COOKIES_MIN) {
539 return apr_psprintf(pool, "maximum must not be less than %d",
540 OIDC_MAX_NUMBER_OF_STATE_COOKIES_MIN);
541 }
542 if (v > OIDC_MAX_NUMBER_OF_STATE_COOKIES_MAX) {
543 return apr_psprintf(pool, "maximum must not be greater than %d",
544 OIDC_MAX_NUMBER_OF_STATE_COOKIES_MAX);
545 }
546 return NULL;
547 }
548
549 /*
550 * parse a session max duration value from the provided string
551 */
oidc_parse_session_max_duration(apr_pool_t * pool,const char * arg,int * int_value)552 const char *oidc_parse_session_max_duration(apr_pool_t *pool, const char *arg,
553 int *int_value) {
554 return oidc_parse_int_valid(pool, arg, int_value,
555 oidc_valid_session_max_duration);
556 }
557
558 /*
559 * parse a base64 encoded binary value from the provided string
560 */
oidc_parse_base64(apr_pool_t * pool,const char * input,char ** output,int * output_len)561 char *oidc_parse_base64(apr_pool_t *pool, const char *input, char **output,
562 int *output_len) {
563 int len = apr_base64_decode_len(input);
564 *output = apr_palloc(pool, len);
565 *output_len = apr_base64_decode(*output, input);
566 if (*output_len <= 0)
567 return apr_psprintf(pool, "base64-decoding of \"%s\" failed", input);
568 return NULL;
569 }
570
571 /*
572 * parse a base64url encoded binary value from the provided string
573 */
oidc_parse_base64url(apr_pool_t * pool,const char * input,char ** output,int * output_len)574 static char *oidc_parse_base64url(apr_pool_t *pool, const char *input,
575 char **output, int *output_len) {
576 *output_len = oidc_base64url_decode(pool, output, input);
577 if (*output_len <= 0)
578 return apr_psprintf(pool, "base64url-decoding of \"%s\" failed", input);
579 return NULL;
580 }
581
582 /*
583 * parse a hexadecimal encoded binary value from the provided string
584 */
oidc_parse_hex(apr_pool_t * pool,const char * input,char ** output,int * output_len)585 static char *oidc_parse_hex(apr_pool_t *pool, const char *input, char **output,
586 int *output_len) {
587 *output_len = strlen(input) / 2;
588 const char *pos = input;
589 unsigned char *val = apr_palloc(pool, *output_len);
590 size_t count = 0;
591 for (count = 0; count < (*output_len) / sizeof(unsigned char); count++) {
592 sscanf(pos, "%2hhx", &val[count]);
593 pos += 2;
594 }
595 *output = (char*) val;
596 return NULL;
597 }
598
599 #define OIDC_KEY_ENCODING_BASE64 "b64"
600 #define OIDC_KEY_ENCODING_BASE64_URL "b64url"
601 #define OIDC_KEY_ENCODING_HEX "hex"
602 #define OIDC_KEY_ENCODING_PLAIN "plain"
603
604 /*
605 * parse a key value based on the provided encoding: b64|b64url|hex|plain
606 */
oidc_parse_key_value(apr_pool_t * pool,const char * enc,const char * input,char ** key,int * key_len)607 static const char *oidc_parse_key_value(apr_pool_t *pool, const char *enc,
608 const char *input, char **key, int *key_len) {
609 static char *options[] = {
610 OIDC_KEY_ENCODING_BASE64,
611 OIDC_KEY_ENCODING_BASE64_URL,
612 OIDC_KEY_ENCODING_HEX,
613 OIDC_KEY_ENCODING_PLAIN,
614 NULL };
615 const char *rv = oidc_valid_string_option(pool, enc, options);
616 if (rv != NULL)
617 return rv;
618 if (apr_strnatcmp(enc, OIDC_KEY_ENCODING_BASE64) == 0)
619 return oidc_parse_base64(pool, input, key, key_len);
620 if (apr_strnatcmp(enc, OIDC_KEY_ENCODING_BASE64_URL) == 0)
621 return oidc_parse_base64url(pool, input, key, key_len);
622 if (apr_strnatcmp(enc, OIDC_KEY_ENCODING_HEX) == 0)
623 return oidc_parse_hex(pool, input, key, key_len);
624 if (apr_strnatcmp(enc, OIDC_KEY_ENCODING_PLAIN) == 0) {
625 *key = apr_pstrdup(pool, input);
626 *key_len = strlen(*key);
627 }
628 return NULL;
629 }
630
631 #define OIDC_KEY_TUPLE_SEPARATOR "#"
632
633 /*
634 * parse a <encoding>#<key-identifier>#<key> tuple
635 */
oidc_parse_enc_kid_key_tuple(apr_pool_t * pool,const char * tuple,char ** kid,char ** key,int * key_len,apr_byte_t triplet)636 const char *oidc_parse_enc_kid_key_tuple(apr_pool_t *pool, const char *tuple,
637 char **kid, char **key, int *key_len, apr_byte_t triplet) {
638 const char *rv = NULL;
639 char *s = NULL, *p = NULL, *q = NULL, *enc = NULL;
640
641 if ((tuple == NULL) || (apr_strnatcmp(tuple, "") == 0))
642 return "tuple value not set";
643
644 s = apr_pstrdup(pool, tuple);
645 p = strstr(s, OIDC_KEY_TUPLE_SEPARATOR);
646 if (p && triplet)
647 q = strstr(p + 1, OIDC_KEY_TUPLE_SEPARATOR);
648
649 if (p) {
650 if (q) {
651 *p = '\0';
652 *q = '\0';
653 enc = s;
654 p++;
655 if (p != q)
656 *kid = apr_pstrdup(pool, p);
657 rv = oidc_parse_key_value(pool, enc, q + 1, key, key_len);
658 } else {
659 *p = '\0';
660 *kid = s;
661 *key = p + 1;
662 *key_len = strlen(*key);
663 }
664 } else {
665 *kid = NULL;
666 *key = s;
667 *key_len = strlen(*key);
668 }
669
670 return rv;
671 }
672
673 #define OIDC_PASS_ID_TOKEN_AS_CLAIMS_STR "claims"
674 #define OIDC_PASS_IDTOKEN_AS_PAYLOAD_STR "payload"
675 #define OIDC_PASS_IDTOKEN_AS_SERIALIZED_STR "serialized"
676
677 /*
678 * convert a "pass id token as" value to an integer
679 */
oidc_parse_pass_idtoken_as_str2int(const char * v)680 static int oidc_parse_pass_idtoken_as_str2int(const char *v) {
681 if (apr_strnatcmp(v, OIDC_PASS_ID_TOKEN_AS_CLAIMS_STR) == 0)
682 return OIDC_PASS_IDTOKEN_AS_CLAIMS;
683 if (apr_strnatcmp(v, OIDC_PASS_IDTOKEN_AS_PAYLOAD_STR) == 0)
684 return OIDC_PASS_IDTOKEN_AS_PAYLOAD;
685 if (apr_strnatcmp(v, OIDC_PASS_IDTOKEN_AS_SERIALIZED_STR) == 0)
686 return OIDC_PASS_IDTOKEN_AS_SERIALIZED;
687 return -1;
688 }
689
690 /*
691 * parse a "pass id token as" value from the provided strings
692 */
oidc_parse_pass_idtoken_as(apr_pool_t * pool,const char * v1,const char * v2,const char * v3,int * int_value)693 const char *oidc_parse_pass_idtoken_as(apr_pool_t *pool, const char *v1,
694 const char *v2, const char *v3, int *int_value) {
695 static char *options[] = {
696 OIDC_PASS_ID_TOKEN_AS_CLAIMS_STR,
697 OIDC_PASS_IDTOKEN_AS_PAYLOAD_STR,
698 OIDC_PASS_IDTOKEN_AS_SERIALIZED_STR,
699 NULL };
700 const char *rv = NULL;
701 rv = oidc_valid_string_option(pool, v1, options);
702 if (rv != NULL)
703 return rv;
704 *int_value = oidc_parse_pass_idtoken_as_str2int(v1);
705
706 if (v2 == NULL)
707 return NULL;
708
709 rv = oidc_valid_string_option(pool, v2, options);
710 if (rv != NULL)
711 return rv;
712 *int_value |= oidc_parse_pass_idtoken_as_str2int(v2);
713
714 if (v3 == NULL)
715 return NULL;
716
717 rv = oidc_valid_string_option(pool, v3, options);
718 if (rv != NULL)
719 return rv;
720 *int_value |= oidc_parse_pass_idtoken_as_str2int(v3);
721
722 return NULL;
723 }
724
725 #define OIDC_PASS_USERINFO_AS_CLAIMS_STR "claims"
726 #define OIDC_PASS_USERINFO_AS_JSON_OBJECT_STR "json"
727 #define OIDC_PASS_USERINFO_AS_JWT_STR "jwt"
728
729 /*
730 * convert a "pass userinfo as" value to an integer
731 */
oidc_parse_pass_userinfo_as_str2int(const char * v)732 static int oidc_parse_pass_userinfo_as_str2int(const char *v) {
733 if (apr_strnatcmp(v, OIDC_PASS_USERINFO_AS_CLAIMS_STR) == 0)
734 return OIDC_PASS_USERINFO_AS_CLAIMS;
735 if (apr_strnatcmp(v, OIDC_PASS_USERINFO_AS_JSON_OBJECT_STR) == 0)
736 return OIDC_PASS_USERINFO_AS_JSON_OBJECT;
737 if (apr_strnatcmp(v, OIDC_PASS_USERINFO_AS_JWT_STR) == 0)
738 return OIDC_PASS_USERINFO_AS_JWT;
739 return -1;
740 }
741
742 /*
743 * parse a "pass id token as" value from the provided strings
744 */
oidc_parse_pass_userinfo_as(apr_pool_t * pool,const char * v1,const char * v2,const char * v3,int * int_value)745 const char *oidc_parse_pass_userinfo_as(apr_pool_t *pool, const char *v1,
746 const char *v2, const char *v3, int *int_value) {
747 static char *options[] = {
748 OIDC_PASS_USERINFO_AS_CLAIMS_STR,
749 OIDC_PASS_USERINFO_AS_JSON_OBJECT_STR,
750 OIDC_PASS_USERINFO_AS_JWT_STR,
751 NULL };
752 const char *rv = NULL;
753 rv = oidc_valid_string_option(pool, v1, options);
754 if (rv != NULL)
755 return rv;
756 *int_value = oidc_parse_pass_userinfo_as_str2int(v1);
757
758 if (v2 == NULL)
759 return NULL;
760
761 rv = oidc_valid_string_option(pool, v2, options);
762 if (rv != NULL)
763 return rv;
764 *int_value |= oidc_parse_pass_userinfo_as_str2int(v2);
765
766 if (v3 == NULL)
767 return NULL;
768
769 rv = oidc_valid_string_option(pool, v3, options);
770 if (rv != NULL)
771 return rv;
772 *int_value |= oidc_parse_pass_userinfo_as_str2int(v3);
773
774 return NULL;
775 }
776
777 #define OIDC_LOGOUT_ON_ERROR_REFRESH_STR "logout_on_error"
778
779 /*
780 * convert a "logout_on_error" to an integer
781 */
oidc_parse_logout_on_error_refresh_as_str2int(const char * v)782 static int oidc_parse_logout_on_error_refresh_as_str2int(const char *v) {
783 if (apr_strnatcmp(v, OIDC_LOGOUT_ON_ERROR_REFRESH_STR) == 0)
784 return OIDC_LOGOUT_ON_ERROR_REFRESH;
785 return OIDC_CONFIG_POS_INT_UNSET;
786 }
787
788 /*
789 * parse a "logout_on_error" value from the provided strings
790 */
oidc_parse_logout_on_error_refresh_as(apr_pool_t * pool,const char * v1,int * int_value)791 const char *oidc_parse_logout_on_error_refresh_as(apr_pool_t *pool, const char *v1,
792 int *int_value) {
793 static char *options[] = {
794 OIDC_LOGOUT_ON_ERROR_REFRESH_STR,
795 NULL };
796 const char *rv = NULL;
797 rv = oidc_valid_string_option(pool, v1, options);
798 if (rv != NULL)
799 return rv;
800 *int_value = oidc_parse_logout_on_error_refresh_as_str2int(v1);
801
802 return NULL;
803 }
804
805 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER_STR "header"
806 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_POST_STR "post"
807 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY_STR "query"
808 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_STR "cookie"
809 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_BASIC_STR "basic"
810
811 /*
812 * convert an "accept OAuth 2.0 token in" byte value to a string representation
813 */
oidc_accept_oauth_token_in2str(apr_pool_t * pool,apr_byte_t v)814 const char *oidc_accept_oauth_token_in2str(apr_pool_t *pool, apr_byte_t v) {
815 static char *options[] = { NULL, NULL, NULL, NULL, NULL };
816 int i = 0;
817 if (v & OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER) {
818 options[i] = OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER_STR;
819 i++;
820 }
821 if (v & OIDC_OAUTH_ACCEPT_TOKEN_IN_POST) {
822 options[i] = OIDC_OAUTH_ACCEPT_TOKEN_IN_POST_STR;
823 i++;
824 }
825 if (v & OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY) {
826 options[i] = OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY_STR;
827 i++;
828 }
829 if (v & OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE) {
830 options[i] = OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_STR;
831 i++;
832 }
833 if (v & OIDC_OAUTH_ACCEPT_TOKEN_IN_BASIC) {
834 options[i] = OIDC_OAUTH_ACCEPT_TOKEN_IN_BASIC_STR;
835 i++;
836 }
837 return oidc_flatten_list_options(pool, options);
838 }
839
840 /*
841 * convert an "accept OAuth 2.0 token in" value to an integer
842 */
oidc_parse_oauth_accept_token_in_str2byte(const char * v)843 static apr_byte_t oidc_parse_oauth_accept_token_in_str2byte(const char *v) {
844 if (apr_strnatcmp(v, OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER_STR) == 0)
845 return OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER;
846 if (apr_strnatcmp(v, OIDC_OAUTH_ACCEPT_TOKEN_IN_POST_STR) == 0)
847 return OIDC_OAUTH_ACCEPT_TOKEN_IN_POST;
848 if (apr_strnatcmp(v, OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY_STR) == 0)
849 return OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY;
850 if (strstr(v, OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_STR) == v)
851 return OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE;
852 if (strstr(v, OIDC_OAUTH_ACCEPT_TOKEN_IN_BASIC_STR) == v)
853 return OIDC_OAUTH_ACCEPT_TOKEN_IN_BASIC;
854 return OIDC_OAUTH_ACCEPT_TOKEN_IN_DEFAULT;
855 }
856
857 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_NAME_DEFAULT "PA.global"
858 #define OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_SEPARATOR ":"
859
860 /*
861 * parse an "accept OAuth 2.0 token in" value from the provided string
862 */
oidc_parse_accept_oauth_token_in(apr_pool_t * pool,const char * arg,int * b_value,apr_hash_t * list_options)863 const char *oidc_parse_accept_oauth_token_in(apr_pool_t *pool, const char *arg,
864 int *b_value, apr_hash_t *list_options) {
865 static char *options[] = {
866 OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER_STR,
867 OIDC_OAUTH_ACCEPT_TOKEN_IN_POST_STR,
868 OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY_STR,
869 OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_STR,
870 OIDC_OAUTH_ACCEPT_TOKEN_IN_BASIC_STR,
871 NULL };
872 const char *rv = NULL;
873
874 const char *s = apr_pstrdup(pool, arg);
875 char *p = strstr(s, OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_SEPARATOR);
876
877 if (p != NULL) {
878 *p = '\0';
879 p++;
880 } else {
881 p = OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE_NAME_DEFAULT;
882 }
883
884 rv = oidc_valid_string_option(pool, s, options);
885 if (rv != NULL)
886 return rv;
887
888 int v = oidc_parse_oauth_accept_token_in_str2byte(s);
889 if (*b_value == OIDC_CONFIG_POS_INT_UNSET)
890 *b_value = v;
891 else
892 *b_value |= v;
893
894 if (v == OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE) {
895 apr_hash_set(list_options,
896 OIDC_OAUTH_ACCEPT_TOKEN_IN_OPTION_COOKIE_NAME,
897 APR_HASH_KEY_STRING, p);
898 }
899
900 return NULL;
901 }
902
903 /*
904 * check if the specified string is a valid claim formatting configuration value
905 */
oidc_valid_claim_format(apr_pool_t * pool,const char * arg)906 const char *oidc_valid_claim_format(apr_pool_t *pool, const char *arg) {
907 static char *options[] = {
908 OIDC_CLAIM_FORMAT_RELATIVE,
909 OIDC_CLAIM_FORMAT_ABSOLUTE,
910 NULL };
911 return oidc_valid_string_option(pool, arg, options);
912 }
913
914 /*
915 * parse a "claim required" value from the provided string
916 */
oidc_parse_claim_required(apr_pool_t * pool,const char * arg,int * is_required)917 const char *oidc_parse_claim_required(apr_pool_t *pool, const char *arg,
918 int *is_required) {
919 static char *options[] = {
920 OIDC_CLAIM_REQUIRED_MANDATORY,
921 OIDC_CLAIM_REQUIRED_OPTIONAL,
922 NULL };
923 const char *rv = oidc_valid_string_option(pool, arg, options);
924 if (rv != NULL)
925 return rv;
926 *is_required = (apr_strnatcmp(arg, OIDC_CLAIM_REQUIRED_MANDATORY) == 0);
927 return NULL;
928 }
929
930 /*
931 * check if the provided string is a valid HTTP method for the OAuth token introspection endpoint
932 */
oidc_valid_introspection_method(apr_pool_t * pool,const char * arg)933 const char *oidc_valid_introspection_method(apr_pool_t *pool, const char *arg) {
934 static char *options[] = {
935 OIDC_INTROSPECTION_METHOD_GET,
936 OIDC_INTROSPECTION_METHOD_POST,
937 NULL };
938 return oidc_valid_string_option(pool, arg, options);
939 }
940
941 #define OIDC_PASS_CLAIMS_AS_BOTH "both"
942 #define OIDC_PASS_CLAIMS_AS_HEADERS "headers"
943 #define OIDC_PASS_CLAIMS_AS_ENV "environment"
944 #define OIDC_PASS_CLAIMS_AS_NONE "none"
945
946 /*
947 * parse a "set claims as" value from the provided string
948 */
oidc_parse_set_claims_as(apr_pool_t * pool,const char * arg,int * in_headers,int * in_env_vars)949 const char *oidc_parse_set_claims_as(apr_pool_t *pool, const char *arg,
950 int *in_headers, int *in_env_vars) {
951 static char *options[] = {
952 OIDC_PASS_CLAIMS_AS_BOTH,
953 OIDC_PASS_CLAIMS_AS_HEADERS,
954 OIDC_PASS_CLAIMS_AS_ENV,
955 OIDC_PASS_CLAIMS_AS_NONE,
956 NULL };
957 const char *rv = oidc_valid_string_option(pool, arg, options);
958 if (rv != NULL)
959 return rv;
960
961 if (apr_strnatcmp(arg, OIDC_PASS_CLAIMS_AS_BOTH) == 0) {
962 *in_headers = 1;
963 *in_env_vars = 1;
964 } else if (apr_strnatcmp(arg, OIDC_PASS_CLAIMS_AS_HEADERS) == 0) {
965 *in_headers = 1;
966 *in_env_vars = 0;
967 } else if (apr_strnatcmp(arg, OIDC_PASS_CLAIMS_AS_ENV) == 0) {
968 *in_headers = 0;
969 *in_env_vars = 1;
970 } else if (apr_strnatcmp(arg, OIDC_PASS_CLAIMS_AS_NONE) == 0) {
971 *in_headers = 0;
972 *in_env_vars = 0;
973 }
974
975 return NULL;
976 }
977
978 #define OIDC_UNAUTH_ACTION_AUTH_STR "auth"
979 #define OIDC_UNAUTH_ACTION_PASS_STR "pass"
980 #define OIDC_UNAUTH_ACTION_401_STR "401"
981 #define OIDC_UNAUTH_ACTION_407_STR "407"
982 #define OIDC_UNAUTH_ACTION_410_STR "410"
983
984 /*
985 * parse an "unauthenticated action" value from the provided string
986 */
oidc_parse_unauth_action(apr_pool_t * pool,const char * arg,int * action)987 const char *oidc_parse_unauth_action(apr_pool_t *pool, const char *arg,
988 int *action) {
989 static char *options[] = {
990 OIDC_UNAUTH_ACTION_AUTH_STR,
991 OIDC_UNAUTH_ACTION_PASS_STR,
992 OIDC_UNAUTH_ACTION_401_STR,
993 OIDC_UNAUTH_ACTION_407_STR,
994 OIDC_UNAUTH_ACTION_410_STR,
995 NULL };
996 const char *rv = oidc_valid_string_option(pool, arg, options);
997 if (rv != NULL)
998 return rv;
999
1000 if (apr_strnatcmp(arg, OIDC_UNAUTH_ACTION_AUTH_STR) == 0)
1001 *action = OIDC_UNAUTH_AUTHENTICATE;
1002 else if (apr_strnatcmp(arg, OIDC_UNAUTH_ACTION_PASS_STR) == 0)
1003 *action = OIDC_UNAUTH_PASS;
1004 else if (apr_strnatcmp(arg, OIDC_UNAUTH_ACTION_401_STR) == 0)
1005 *action = OIDC_UNAUTH_RETURN401;
1006 else if (apr_strnatcmp(arg, OIDC_UNAUTH_ACTION_407_STR) == 0)
1007 *action = OIDC_UNAUTH_RETURN407;
1008 else if (apr_strnatcmp(arg, OIDC_UNAUTH_ACTION_410_STR) == 0)
1009 *action = OIDC_UNAUTH_RETURN410;
1010
1011 return NULL;
1012 }
1013
1014 #define OIDC_UNAUTZ_ACTION_AUTH_STR "auth"
1015 #define OIDC_UNAUTZ_ACTION_401_STR "401"
1016 #define OIDC_UNAUTZ_ACTION_403_STR "403"
1017
1018 /*
1019 * parse an "unauthorized action" value from the provided string
1020 */
oidc_parse_unautz_action(apr_pool_t * pool,const char * arg,int * action)1021 const char *oidc_parse_unautz_action(apr_pool_t *pool, const char *arg,
1022 int *action) {
1023 static char *options[] = {
1024 OIDC_UNAUTZ_ACTION_AUTH_STR,
1025 OIDC_UNAUTZ_ACTION_401_STR,
1026 OIDC_UNAUTZ_ACTION_403_STR,
1027 NULL };
1028 const char *rv = oidc_valid_string_option(pool, arg, options);
1029 if (rv != NULL)
1030 return rv;
1031
1032 if (apr_strnatcmp(arg, OIDC_UNAUTZ_ACTION_AUTH_STR) == 0)
1033 *action = OIDC_UNAUTZ_AUTHENTICATE;
1034 else if (apr_strnatcmp(arg, OIDC_UNAUTZ_ACTION_401_STR) == 0)
1035 *action = OIDC_UNAUTZ_RETURN401;
1036 else if (apr_strnatcmp(arg, OIDC_UNAUTZ_ACTION_403_STR) == 0)
1037 *action = OIDC_UNAUTZ_RETURN403;
1038
1039 return NULL;
1040 }
1041
1042 /*
1043 * check if there's a valid entry in a string of arrays, with a preference
1044 */
oidc_valid_string_in_array(apr_pool_t * pool,json_t * json,const char * key,oidc_valid_function_t valid_function,char ** value,apr_byte_t optional,const char * preference)1045 const char *oidc_valid_string_in_array(apr_pool_t *pool, json_t *json,
1046 const char *key, oidc_valid_function_t valid_function, char **value,
1047 apr_byte_t optional, const char *preference) {
1048 int i = 0;
1049 json_t *json_arr = json_object_get(json, key);
1050 apr_byte_t found = FALSE;
1051 if ((json_arr != NULL) && (json_is_array(json_arr))) {
1052 for (i = 0; i < json_array_size(json_arr); i++) {
1053 json_t *elem = json_array_get(json_arr, i);
1054 if (!json_is_string(elem)) {
1055 return apr_psprintf(pool,
1056 "unhandled in-array JSON non-string object type [%d]",
1057 elem->type);
1058 continue;
1059 }
1060 if (valid_function(pool, json_string_value(elem)) == NULL) {
1061 found = TRUE;
1062 if (value != NULL) {
1063 if ((preference != NULL)
1064 && (apr_strnatcmp(json_string_value(elem),
1065 preference) == 0)) {
1066 *value = apr_pstrdup(pool, json_string_value(elem));
1067 break;
1068 }
1069 if (*value == NULL) {
1070 *value = apr_pstrdup(pool, json_string_value(elem));
1071 }
1072 }
1073 }
1074 }
1075 if (found == FALSE) {
1076 return apr_psprintf(pool,
1077 "could not find a valid array string element for entry \"%s\"",
1078 key);
1079 }
1080 } else if (optional == FALSE) {
1081 return apr_psprintf(pool, "JSON object did not contain a \"%s\" array",
1082 key);
1083 }
1084 return NULL;
1085 }
1086
1087 #define OIDC_JWKS_REFRESH_INTERVAL_MIN 300
1088 #define OIDC_JWKS_REFRESH_INTERVAL_MAX 3600 * 24 * 365
1089
1090 /*
1091 * check the boundaries for JWKs refresh interval
1092 */
oidc_valid_jwks_refresh_interval(apr_pool_t * pool,int v)1093 const char *oidc_valid_jwks_refresh_interval(apr_pool_t *pool, int v) {
1094 return oidc_valid_int_min_max(pool, v, OIDC_JWKS_REFRESH_INTERVAL_MIN,
1095 OIDC_JWKS_REFRESH_INTERVAL_MAX);
1096 }
1097
1098 /*
1099 * parse a JWKs refresh interval from the provided string
1100 */
oidc_parse_jwks_refresh_interval(apr_pool_t * pool,const char * arg,int * int_value)1101 const char *oidc_parse_jwks_refresh_interval(apr_pool_t *pool, const char *arg,
1102 int *int_value) {
1103 return oidc_parse_int_valid(pool, arg, int_value,
1104 oidc_valid_jwks_refresh_interval);
1105 }
1106
1107 #define OIDC_IDTOKEN_IAT_SLACK_MIN 0
1108 #define OIDC_IDTOKEN_IAT_SLACK_MAX 3600
1109
1110 /*
1111 * check the boundaries for ID token "issued-at" (iat) timestamp slack
1112 */
oidc_valid_idtoken_iat_slack(apr_pool_t * pool,int v)1113 const char *oidc_valid_idtoken_iat_slack(apr_pool_t *pool, int v) {
1114 return oidc_valid_int_min_max(pool, v, OIDC_IDTOKEN_IAT_SLACK_MIN,
1115 OIDC_IDTOKEN_IAT_SLACK_MAX);
1116 }
1117
1118 /*
1119 * parse an ID token "iat" slack interval
1120 */
oidc_parse_idtoken_iat_slack(apr_pool_t * pool,const char * arg,int * int_value)1121 const char *oidc_parse_idtoken_iat_slack(apr_pool_t *pool, const char *arg,
1122 int *int_value) {
1123 return oidc_parse_int_valid(pool, arg, int_value,
1124 oidc_valid_idtoken_iat_slack);
1125 }
1126
1127 #define OIDC_USERINFO_REFRESH_INTERVAL_MIN 0
1128 #define OIDC_USERINFO_REFRESH_INTERVAL_MAX 3600 * 24 * 365
1129
1130 /*
1131 * check the boundaries for the userinfo refresh interval
1132 */
oidc_valid_userinfo_refresh_interval(apr_pool_t * pool,int v)1133 const char *oidc_valid_userinfo_refresh_interval(apr_pool_t *pool, int v) {
1134 return oidc_valid_int_min_max(pool, v, OIDC_USERINFO_REFRESH_INTERVAL_MIN,
1135 OIDC_USERINFO_REFRESH_INTERVAL_MAX);
1136 }
1137
1138 /*
1139 * parse a userinfo refresh interval from the provided string
1140 */
oidc_parse_userinfo_refresh_interval(apr_pool_t * pool,const char * arg,int * int_value)1141 const char *oidc_parse_userinfo_refresh_interval(apr_pool_t *pool,
1142 const char *arg, int *int_value) {
1143 return oidc_parse_int_valid(pool, arg, int_value,
1144 oidc_valid_userinfo_refresh_interval);
1145 }
1146
1147 #define OIDC_USER_INFO_TOKEN_METHOD_HEADER_STR "authz_header"
1148 #define OIDC_USER_INFO_TOKEN_METHOD_POST_STR "post_param"
1149
1150 /*
1151 * check if the provided string is a valid userinfo token presentation method
1152 */
oidc_valid_userinfo_token_method(apr_pool_t * pool,const char * arg)1153 const char *oidc_valid_userinfo_token_method(apr_pool_t *pool, const char *arg) {
1154 static char *options[] = {
1155 OIDC_USER_INFO_TOKEN_METHOD_HEADER_STR,
1156 OIDC_USER_INFO_TOKEN_METHOD_POST_STR,
1157 NULL };
1158 return oidc_valid_string_option(pool, arg, options);
1159 }
1160
1161 /*
1162 * parse a userinfo token method string value to an integer
1163 */
oidc_parse_userinfo_token_method(apr_pool_t * pool,const char * arg,int * int_value)1164 const char *oidc_parse_userinfo_token_method(apr_pool_t *pool, const char *arg,
1165 int *int_value) {
1166 const char *rv = oidc_valid_userinfo_token_method(pool, arg);
1167 if (rv != NULL)
1168 return rv;
1169
1170 if (apr_strnatcmp(arg, OIDC_USER_INFO_TOKEN_METHOD_HEADER_STR) == 0)
1171 *int_value = OIDC_USER_INFO_TOKEN_METHOD_HEADER;
1172 if (apr_strnatcmp(arg, OIDC_USER_INFO_TOKEN_METHOD_POST_STR) == 0)
1173 *int_value = OIDC_USER_INFO_TOKEN_METHOD_POST;
1174
1175 return NULL;
1176 }
1177
1178 /*
1179 * parse an "info hook data" value from the provided string
1180 */
oidc_parse_info_hook_data(apr_pool_t * pool,const char * arg,apr_hash_t ** hook_data)1181 const char *oidc_parse_info_hook_data(apr_pool_t *pool, const char *arg,
1182 apr_hash_t **hook_data) {
1183 static char *options[] = {
1184 OIDC_HOOK_INFO_TIMESTAMP,
1185 OIDC_HOOK_INFO_ACCES_TOKEN,
1186 OIDC_HOOK_INFO_ACCES_TOKEN_EXP,
1187 OIDC_HOOK_INFO_ID_TOKEN,
1188 OIDC_HOOK_INFO_USER_INFO,
1189 OIDC_HOOK_INFO_REFRESH_TOKEN,
1190 OIDC_HOOK_INFO_SESSION_EXP,
1191 OIDC_HOOK_INFO_SESSION_TIMEOUT,
1192 OIDC_HOOK_INFO_SESSION_REMOTE_USER,
1193 OIDC_HOOK_INFO_SESSION,
1194 NULL };
1195 const char *rv = oidc_valid_string_option(pool, arg, options);
1196 if (rv != NULL)
1197 return rv;
1198 if (*hook_data == NULL)
1199 *hook_data = apr_hash_make(pool);
1200 apr_hash_set(*hook_data, arg, APR_HASH_KEY_STRING, arg);
1201
1202 return NULL;
1203 }
1204
1205 #define OIDC_TOKEN_BINDING_POLICY_DISABLED_STR "disabled"
1206 #define OIDC_TOKEN_BINDING_POLICY_OPTIONAL_STR "optional"
1207 #define OIDC_TOKEN_BINDING_POLICY_REQUIRED_STR "required"
1208 #define OIDC_TOKEN_BINDING_POLICY_ENFORCED_STR "enforced"
1209
oidc_token_binding_policy2str(apr_pool_t * pool,int v)1210 const char *oidc_token_binding_policy2str(apr_pool_t *pool, int v) {
1211 if (v == OIDC_TOKEN_BINDING_POLICY_DISABLED)
1212 return OIDC_TOKEN_BINDING_POLICY_DISABLED;
1213 if (v == OIDC_TOKEN_BINDING_POLICY_OPTIONAL)
1214 return OIDC_TOKEN_BINDING_POLICY_OPTIONAL_STR;
1215 if (v == OIDC_TOKEN_BINDING_POLICY_REQUIRED)
1216 return OIDC_TOKEN_BINDING_POLICY_REQUIRED_STR;
1217 if (v == OIDC_TOKEN_BINDING_POLICY_ENFORCED)
1218 return OIDC_TOKEN_BINDING_POLICY_ENFORCED_STR;
1219 return NULL;
1220 }
1221
1222 /*
1223 * check token binding policy string value
1224 */
oidc_valid_token_binding_policy(apr_pool_t * pool,const char * arg)1225 const char *oidc_valid_token_binding_policy(apr_pool_t *pool, const char *arg) {
1226 static char *options[] = {
1227 OIDC_TOKEN_BINDING_POLICY_DISABLED_STR,
1228 OIDC_TOKEN_BINDING_POLICY_OPTIONAL_STR,
1229 OIDC_TOKEN_BINDING_POLICY_REQUIRED_STR,
1230 OIDC_TOKEN_BINDING_POLICY_ENFORCED_STR,
1231 NULL };
1232 return oidc_valid_string_option(pool, arg, options);
1233 }
1234
1235 /*
1236 * parse token binding policy
1237 */
oidc_parse_token_binding_policy(apr_pool_t * pool,const char * arg,int * policy)1238 const char *oidc_parse_token_binding_policy(apr_pool_t *pool, const char *arg,
1239 int *policy) {
1240 const char *rv = oidc_valid_token_binding_policy(pool, arg);
1241 if (rv != NULL)
1242 return rv;
1243
1244 if (apr_strnatcmp(arg, OIDC_TOKEN_BINDING_POLICY_DISABLED_STR) == 0)
1245 *policy = OIDC_TOKEN_BINDING_POLICY_DISABLED;
1246 else if (apr_strnatcmp(arg, OIDC_TOKEN_BINDING_POLICY_OPTIONAL_STR) == 0)
1247 *policy = OIDC_TOKEN_BINDING_POLICY_OPTIONAL;
1248 else if (apr_strnatcmp(arg, OIDC_TOKEN_BINDING_POLICY_REQUIRED_STR) == 0)
1249 *policy = OIDC_TOKEN_BINDING_POLICY_REQUIRED;
1250 else if (apr_strnatcmp(arg, OIDC_TOKEN_BINDING_POLICY_ENFORCED_STR) == 0)
1251 *policy = OIDC_TOKEN_BINDING_POLICY_ENFORCED;
1252
1253 return NULL;
1254 }
1255
1256 #define OIDC_AUTH_REQUEST_METHOD_GET_STR "GET"
1257 #define OIDC_AUTH_REQEUST_METHOD_POST_STR "POST"
1258
1259 /*
1260 * parse method for sending the authentication request
1261 */
oidc_valid_auth_request_method(apr_pool_t * pool,const char * arg)1262 const char *oidc_valid_auth_request_method(apr_pool_t *pool, const char *arg) {
1263 static char *options[] = {
1264 OIDC_AUTH_REQUEST_METHOD_GET_STR,
1265 OIDC_AUTH_REQEUST_METHOD_POST_STR,
1266 NULL };
1267 return oidc_valid_string_option(pool, arg, options);
1268 }
1269
1270 /*
1271 * parse method for sending the authentication request
1272 */
oidc_parse_auth_request_method(apr_pool_t * pool,const char * arg,int * method)1273 const char *oidc_parse_auth_request_method(apr_pool_t *pool, const char *arg,
1274 int *method) {
1275 const char *rv = oidc_valid_auth_request_method(pool, arg);
1276 if (rv != NULL)
1277 return rv;
1278
1279 if (apr_strnatcmp(arg, OIDC_AUTH_REQUEST_METHOD_GET_STR) == 0)
1280 *method = OIDC_AUTH_REQUEST_METHOD_GET;
1281 else if (apr_strnatcmp(arg, OIDC_AUTH_REQEUST_METHOD_POST_STR) == 0)
1282 *method = OIDC_AUTH_REQUEST_METHOD_POST;
1283
1284 return NULL;
1285 }
1286
1287 /*
1288 * parse the maximum number of parallel state cookies
1289 */
oidc_parse_max_number_of_state_cookies(apr_pool_t * pool,const char * arg1,const char * arg2,int * int_value,int * bool_value)1290 const char *oidc_parse_max_number_of_state_cookies(apr_pool_t *pool,
1291 const char *arg1, const char *arg2, int *int_value, int *bool_value) {
1292 const char *rv = NULL;
1293
1294 rv = oidc_parse_int_valid(pool, arg1, int_value,
1295 oidc_valid_max_number_of_state_cookies);
1296 if ((rv == NULL) && (arg2 != NULL))
1297 rv = oidc_parse_boolean(pool, arg2, bool_value);
1298 return rv;
1299 }
1300
1301 #define OIDC_REFRESH_ACCESS_TOKEN_BEFORE_EXPIRY_MIN 0
1302 #define OIDC_REFRESH_ACCESS_TOKEN_BEFORE_EXPIRY_MAX 3600 * 24 * 365
1303
1304 /*
1305 * check the boundaries for the refresh access token expiry TTL
1306 */
oidc_valid_refresh_access_token_before_expiry(apr_pool_t * pool,int v)1307 const char *oidc_valid_refresh_access_token_before_expiry(apr_pool_t *pool,
1308 int v) {
1309 return oidc_valid_int_min_max(pool, v,
1310 OIDC_REFRESH_ACCESS_TOKEN_BEFORE_EXPIRY_MIN,
1311 OIDC_REFRESH_ACCESS_TOKEN_BEFORE_EXPIRY_MAX);
1312 }
1313
1314 /*
1315 * parse an access token expiry TTL from the provided string
1316 */
oidc_parse_refresh_access_token_before_expiry(apr_pool_t * pool,const char * arg,int * int_value)1317 const char *oidc_parse_refresh_access_token_before_expiry(apr_pool_t *pool,
1318 const char *arg, int *int_value) {
1319 return oidc_parse_int_valid(pool, arg, int_value,
1320 oidc_valid_refresh_access_token_before_expiry);
1321 }
1322
1323 #define OIDC_STATE_INPUT_HEADERS_AS_BOTH "both"
1324 #define OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT "user-agent"
1325 #define OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR "x-forwarded-for"
1326 #define OIDC_STATE_INPUT_HEADERS_AS_NONE "none"
1327
1328 /*
1329 * parse a "set state input headers as" value from the provided string
1330 */
oidc_parse_set_state_input_headers_as(apr_pool_t * pool,const char * arg,apr_byte_t * state_input_headers)1331 const char *oidc_parse_set_state_input_headers_as(apr_pool_t *pool, const char *arg,
1332 apr_byte_t *state_input_headers) {
1333 static char *options[] = {
1334 OIDC_STATE_INPUT_HEADERS_AS_BOTH,
1335 OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT,
1336 OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR,
1337 OIDC_STATE_INPUT_HEADERS_AS_NONE,
1338 NULL };
1339 const char *rv = oidc_valid_string_option(pool, arg, options);
1340 if (rv != NULL)
1341 return rv;
1342
1343 if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_BOTH) == 0) {
1344 *state_input_headers = OIDC_STATE_INPUT_HEADERS_USER_AGENT | OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR;
1345 } else if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT) == 0) {
1346 *state_input_headers = OIDC_STATE_INPUT_HEADERS_USER_AGENT;
1347 } else if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR) == 0) {
1348 *state_input_headers = OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR;
1349 } else if (apr_strnatcmp(arg, OIDC_STATE_INPUT_HEADERS_AS_NONE) == 0) {
1350 *state_input_headers = 0;
1351 }
1352
1353 return NULL;
1354 }
1355