/src/mod_auth_openidc/src/cfg/cfg.c
Line | Count | Source |
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-2026 ZmartZone Holding BV |
22 | | * All rights reserved. |
23 | | * |
24 | | * DISCLAIMER OF WARRANTIES: |
25 | | * |
26 | | * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT |
27 | | * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING, |
28 | | * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT, |
29 | | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. NOR ARE THERE ANY |
30 | | * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE |
31 | | * USAGE. FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET |
32 | | * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE |
33 | | * WILL BE UNINTERRUPTED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR |
34 | | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
35 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF |
36 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
37 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
38 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | * |
40 | | * @Author: Hans Zandbelt - hans.zandbelt@openidc.com |
41 | | */ |
42 | | |
43 | | #include "cfg/cache.h" |
44 | | #include "cfg/cfg_int.h" |
45 | | #include "cfg/dir.h" |
46 | | #include "cfg/oauth.h" |
47 | | #include "cfg/parse.h" |
48 | | #include "cfg/provider.h" |
49 | | #include "jose.h" |
50 | | #include "metrics.h" |
51 | | #include "proto/proto.h" |
52 | | #include "session.h" |
53 | | #include "util/util.h" |
54 | | #include "util/util_cfg.h" |
55 | | |
56 | | #if OPENSSL_API_COMPAT < 0x10100000L |
57 | | #include <openssl/err.h> |
58 | | #include <openssl/ssl.h> |
59 | | #endif |
60 | | |
61 | 2 | const char *oidc_cfg_string_list_add(apr_pool_t *pool, apr_array_header_t **list, const char *arg) { |
62 | 2 | if (*list == NULL) |
63 | 2 | *list = apr_array_make(pool, 1, sizeof(const char *)); |
64 | 2 | APR_ARRAY_PUSH(*list, const char *) = arg; |
65 | 2 | return NULL; |
66 | 2 | } |
67 | | |
68 | | #define OIDC_DEFAULT_ACTION_ON_USERINFO_ERROR OIDC_ON_ERROR_502 |
69 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(action_on_userinfo_error, oidc_on_error_action_t, OIDC_DEFAULT_ACTION_ON_USERINFO_ERROR) |
70 | | |
71 | | #define OIDC_CFG_MEMBER_FUNCS_HTTP_TIMEOUT(member, def_val) \ |
72 | | const char *oidc_cmd_##member##_set(cmd_parms *cmd, void *ptr, const char *arg1, const char *arg2, \ |
73 | 0 | const char *arg3) { \ |
74 | 0 | oidc_cfg_t *cfg = \ |
75 | 0 | (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); \ |
76 | 0 | const char *rv = oidc_cfg_parse_http_timeout(cmd->pool, arg1, arg2, arg3, &cfg->member); \ |
77 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); \ |
78 | 0 | } \ |
79 | | \ |
80 | 252 | oidc_http_timeout_t *oidc_cfg_##member##_get(oidc_cfg_t *cfg) { \ |
81 | 252 | if (cfg->member.request_timeout == OIDC_CONFIG_POS_INT_UNSET) \ |
82 | 252 | /* NB: we are modifying in-config but post_config/merge has finished by now */ \ |
83 | 252 | cfg->member.request_timeout = def_val; \ |
84 | 252 | return &cfg->member; \ |
85 | 252 | } oidc_cfg_http_timeout_long_get Line | Count | Source | 80 | 252 | oidc_http_timeout_t *oidc_cfg_##member##_get(oidc_cfg_t *cfg) { \ | 81 | 252 | if (cfg->member.request_timeout == OIDC_CONFIG_POS_INT_UNSET) \ | 82 | 252 | /* NB: we are modifying in-config but post_config/merge has finished by now */ \ | 83 | 252 | cfg->member.request_timeout = def_val; \ | 84 | 252 | return &cfg->member; \ | 85 | 252 | } |
Unexecuted instantiation: oidc_cfg_http_timeout_short_get |
86 | | |
87 | | /* request timeout in seconds for HTTP calls that may take a long time */ |
88 | | #define OIDC_DEFAULT_HTTP_REQUEST_TIMEOUT_LONG 30 |
89 | | /* connect timeout in seconds for HTTP calls that may take a long time */ |
90 | 40 | #define OIDC_DEFAULT_HTTP_CONNECT_TIMEOUT_LONG 10 |
91 | | /* nr of retries for HTTP calls that may take a long time */ |
92 | 40 | #define OIDC_DEFAULT_HTTP_RETRIES_LONG 1 |
93 | | /* retry interval in milliseconds for HTTP calls that may take a long time */ |
94 | 40 | #define OIDC_DEFAULT_HTTP_RETRY_INTERVAL_LONG 500 |
95 | | /* timeouts in seconds for HTTP calls that should take a short time (registry/discovery related) */ |
96 | | #define OIDC_DEFAULT_HTTP_REQUEST_TIMEOUT_SHORT 5 |
97 | | /* connect timeout in seconds for HTTP calls that may take a long time */ |
98 | 40 | #define OIDC_DEFAULT_HTTP_CONNECT_TIMEOUT_SHORT 2 |
99 | | /* nr of retries for HTTP calls that should take a short time */ |
100 | 40 | #define OIDC_DEFAULT_HTTP_RETRIES_SHORT 1 |
101 | | /* retry interval in milliseconds for HTTP calls that should take a short time */ |
102 | 40 | #define OIDC_DEFAULT_HTTP_RETRY_INTERVAL_SHORT 300 |
103 | | |
104 | 0 | OIDC_CFG_MEMBER_FUNCS_HTTP_TIMEOUT(http_timeout_long, OIDC_DEFAULT_HTTP_REQUEST_TIMEOUT_LONG) |
105 | 0 | OIDC_CFG_MEMBER_FUNCS_HTTP_TIMEOUT(http_timeout_short, OIDC_DEFAULT_HTTP_REQUEST_TIMEOUT_SHORT) |
106 | | |
107 | 0 | const char *oidc_cmd_crypto_passphrase_set(cmd_parms *cmd, void *struct_ptr, const char *arg1, const char *arg2) { |
108 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
109 | 0 | const char *rv = NULL; |
110 | 0 | char *v = NULL; |
111 | 0 | if (arg1) { |
112 | 0 | rv = oidc_cfg_parse_passphrase(cmd->pool, arg1, &v); |
113 | 0 | if (rv == NULL) |
114 | 0 | cfg->crypto_passphrase.secret1 = v; |
115 | 0 | } |
116 | 0 | if ((rv == NULL) && (arg2 != NULL)) { |
117 | 0 | rv = oidc_cfg_parse_passphrase(cmd->pool, arg2, &v); |
118 | 0 | if (rv == NULL) |
119 | 0 | cfg->crypto_passphrase.secret2 = v; |
120 | 0 | } |
121 | 0 | return rv; |
122 | 0 | } |
123 | | |
124 | 101k | const oidc_crypto_passphrase_t *oidc_cfg_crypto_passphrase_get(const oidc_cfg_t *cfg) { |
125 | 101k | return &cfg->crypto_passphrase; |
126 | 101k | } |
127 | | |
128 | 29.4k | const char *oidc_cfg_crypto_passphrase_secret1_get(const oidc_cfg_t *cfg) { |
129 | 29.4k | return cfg->crypto_passphrase.secret1; |
130 | 29.4k | } |
131 | | |
132 | 0 | void oidc_cfg_crypto_passphrase_secret1_set(oidc_cfg_t *cfg, const char *secret) { |
133 | 0 | cfg->crypto_passphrase.secret1 = secret; |
134 | 0 | } |
135 | | |
136 | 23.2k | const char *oidc_cfg_crypto_passphrase_secret2_get(const oidc_cfg_t *cfg) { |
137 | 23.2k | return cfg->crypto_passphrase.secret2; |
138 | 23.2k | } |
139 | | |
140 | | /* Derive finalized passphrases once, before requests use them for JWE operations. */ |
141 | 0 | apr_byte_t oidc_crypto_passphrase_derive_keys(oidc_crypto_passphrase_t *cp) { |
142 | | /* an empty (non-NULL) secret is treated as "not configured", matching |
143 | | * oidc_util_key_symmetric_create()'s existing behavior for a client_secret */ |
144 | 0 | if ((cp->secret1 != NULL) && (_oidc_strlen(cp->secret1) > 0) && (cp->derived_key1_set == FALSE)) { |
145 | 0 | if (oidc_util_key_derive_passphrase_key(cp->secret1, cp->derived_key1, |
146 | 0 | OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN) == FALSE) |
147 | 0 | return FALSE; |
148 | 0 | cp->derived_key1_set = TRUE; |
149 | 0 | } |
150 | | |
151 | 0 | if ((cp->secret2 != NULL) && (_oidc_strlen(cp->secret2) > 0) && (cp->derived_key2_set == FALSE)) { |
152 | 0 | if (oidc_util_key_derive_passphrase_key(cp->secret2, cp->derived_key2, |
153 | 0 | OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN) == FALSE) |
154 | 0 | return FALSE; |
155 | 0 | cp->derived_key2_set = TRUE; |
156 | 0 | } |
157 | | |
158 | 0 | return TRUE; |
159 | 0 | } |
160 | | |
161 | | /* |
162 | | * thin wrapper around oidc_crypto_passphrase_derive_keys() for the passphrase embedded in a |
163 | | * server config; see that function for details |
164 | | */ |
165 | 0 | apr_byte_t oidc_cfg_crypto_passphrase_derive_keys(oidc_cfg_t *cfg) { |
166 | 0 | return oidc_crypto_passphrase_derive_keys(&cfg->crypto_passphrase); |
167 | 0 | } |
168 | | |
169 | | /* |
170 | | * Memoize PBKDF2 output by secret across vhosts. The pool must outlive kdf_cache; a NULL cache |
171 | | * disables memoization. |
172 | | */ |
173 | | apr_byte_t oidc_crypto_passphrase_derive_keys_cached(apr_pool_t *pool, apr_hash_t *kdf_cache, |
174 | 0 | oidc_crypto_passphrase_t *cp) { |
175 | 0 | const unsigned char *cached = NULL; |
176 | |
|
177 | 0 | if (kdf_cache == NULL) |
178 | 0 | return oidc_crypto_passphrase_derive_keys(cp); |
179 | | |
180 | 0 | if ((cp->secret1 != NULL) && (_oidc_strlen(cp->secret1) > 0) && (cp->derived_key1_set == FALSE)) { |
181 | 0 | cached = apr_hash_get(kdf_cache, cp->secret1, APR_HASH_KEY_STRING); |
182 | 0 | if (cached != NULL) { |
183 | 0 | _oidc_memcpy(cp->derived_key1, cached, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN); |
184 | 0 | } else { |
185 | 0 | if (oidc_util_key_derive_passphrase_key(cp->secret1, cp->derived_key1, |
186 | 0 | OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN) == FALSE) |
187 | 0 | return FALSE; |
188 | 0 | apr_hash_set(kdf_cache, apr_pstrdup(pool, cp->secret1), APR_HASH_KEY_STRING, |
189 | 0 | apr_pmemdup(pool, cp->derived_key1, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN)); |
190 | 0 | } |
191 | 0 | cp->derived_key1_set = TRUE; |
192 | 0 | } |
193 | | |
194 | 0 | if ((cp->secret2 != NULL) && (_oidc_strlen(cp->secret2) > 0) && (cp->derived_key2_set == FALSE)) { |
195 | 0 | cached = apr_hash_get(kdf_cache, cp->secret2, APR_HASH_KEY_STRING); |
196 | 0 | if (cached != NULL) { |
197 | 0 | _oidc_memcpy(cp->derived_key2, cached, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN); |
198 | 0 | } else { |
199 | 0 | if (oidc_util_key_derive_passphrase_key(cp->secret2, cp->derived_key2, |
200 | 0 | OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN) == FALSE) |
201 | 0 | return FALSE; |
202 | 0 | apr_hash_set(kdf_cache, apr_pstrdup(pool, cp->secret2), APR_HASH_KEY_STRING, |
203 | 0 | apr_pmemdup(pool, cp->derived_key2, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN)); |
204 | 0 | } |
205 | 0 | cp->derived_key2_set = TRUE; |
206 | 0 | } |
207 | | |
208 | 0 | return TRUE; |
209 | 0 | } |
210 | | |
211 | | /* |
212 | | * thin wrapper around oidc_crypto_passphrase_derive_keys_cached() for the passphrase embedded |
213 | | * in a server config; see that function for details |
214 | | */ |
215 | 0 | apr_byte_t oidc_cfg_crypto_passphrase_derive_keys_cached(apr_pool_t *pool, apr_hash_t *kdf_cache, oidc_cfg_t *cfg) { |
216 | 0 | return oidc_crypto_passphrase_derive_keys_cached(pool, kdf_cache, &cfg->crypto_passphrase); |
217 | 0 | } |
218 | | |
219 | | const char *oidc_cmd_outgoing_proxy_set(cmd_parms *cmd, void *ptr, const char *arg1, const char *arg2, |
220 | 0 | const char *arg3) { |
221 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
222 | 0 | const char *rv = NULL; |
223 | 0 | if (arg1) |
224 | 0 | cfg->outgoing_proxy.host_port = apr_pstrdup(cmd->pool, arg1); |
225 | 0 | if (arg2) |
226 | 0 | cfg->outgoing_proxy.username_password = apr_pstrdup(cmd->pool, arg2); |
227 | 0 | if (arg3) { |
228 | 0 | rv = oidc_cfg_parse_is_valid_option(cmd->pool, arg3, oidc_http_proxy_auth_options()); |
229 | 0 | if (rv == NULL) |
230 | 0 | cfg->outgoing_proxy.auth_type = oidc_http_proxy_s2auth(arg3); |
231 | 0 | } |
232 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
233 | 0 | } |
234 | | |
235 | 252 | const oidc_http_outgoing_proxy_t *oidc_cfg_outgoing_proxy_get(const oidc_cfg_t *cfg) { |
236 | 252 | return &cfg->outgoing_proxy; |
237 | 252 | } |
238 | | |
239 | 0 | static const char *oidc_cfg_valid_cookie_domain(apr_pool_t *pool, const char *arg) { |
240 | 0 | size_t limit = _oidc_strlen(arg); |
241 | 0 | char d; |
242 | 0 | for (size_t sz = 0; sz < limit; sz++) { |
243 | 0 | d = arg[sz]; |
244 | 0 | if ((d < '0' || d > '9') && (d < 'a' || d > 'z') && (d < 'A' || d > 'Z') && d != '.' && d != '-') { |
245 | 0 | return (apr_psprintf(pool, "invalid character '%c' in cookie domain value: %s", d, arg)); |
246 | 0 | } |
247 | 0 | } |
248 | 0 | return NULL; |
249 | 0 | } |
250 | | |
251 | 0 | OIDC_CFG_MEMBER_FUNCS_TYPE(cookie_domain, const char *, oidc_cfg_valid_cookie_domain(cmd->pool, arg)) |
252 | 0 |
|
253 | 0 | #define OIDC_SESSION_TYPE_SERVER_CACHE_STR "server-cache" |
254 | 0 | #define OIDC_SESSION_TYPE_CLIENT_COOKIE_STR "client-cookie" |
255 | 0 | #define OIDC_SESSION_TYPE_PERSISTENT "persistent" |
256 | 0 | #define OIDC_SESSION_TYPE_STORE_ID_TOKEN "store_id_token" |
257 | 0 | #define OIDC_SESSION_TYPE_SEPARATOR ":" |
258 | | |
259 | 0 | const char *oidc_cmd_session_type_set(cmd_parms *cmd, void *ptr, const char *arg) { |
260 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
261 | |
|
262 | 0 | static const char *options[] = { |
263 | 0 | OIDC_SESSION_TYPE_SERVER_CACHE_STR, |
264 | 0 | OIDC_SESSION_TYPE_SERVER_CACHE_STR OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_PERSISTENT, |
265 | 0 | OIDC_SESSION_TYPE_CLIENT_COOKIE_STR, |
266 | 0 | OIDC_SESSION_TYPE_CLIENT_COOKIE_STR OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_PERSISTENT, |
267 | 0 | OIDC_SESSION_TYPE_CLIENT_COOKIE_STR OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_STORE_ID_TOKEN, |
268 | 0 | OIDC_SESSION_TYPE_CLIENT_COOKIE_STR OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_PERSISTENT |
269 | 0 | OIDC_SESSION_TYPE_SEPARATOR OIDC_SESSION_TYPE_STORE_ID_TOKEN, |
270 | 0 | NULL}; |
271 | |
|
272 | 0 | const char *rv = oidc_cfg_parse_is_valid_option(cmd->pool, arg, options); |
273 | 0 | if (rv != NULL) |
274 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
275 | | |
276 | 0 | const char *s = apr_pstrdup(cmd->pool, arg); |
277 | 0 | char *p = _oidc_strstr(s, OIDC_SESSION_TYPE_SEPARATOR); |
278 | |
|
279 | 0 | if (p) { |
280 | 0 | *p = '\0'; |
281 | 0 | p++; |
282 | 0 | } |
283 | |
|
284 | 0 | if (_oidc_strcmp(s, OIDC_SESSION_TYPE_SERVER_CACHE_STR) == 0) { |
285 | 0 | cfg->session_type = OIDC_SESSION_TYPE_SERVER_CACHE; |
286 | 0 | } else if (_oidc_strcmp(s, OIDC_SESSION_TYPE_CLIENT_COOKIE_STR) == 0) { |
287 | 0 | cfg->session_type = OIDC_SESSION_TYPE_CLIENT_COOKIE; |
288 | 0 | cfg->store_id_token = 0; |
289 | 0 | } |
290 | | |
291 | | /* the combination was validated against options[] above, so each remaining |
292 | | * separator-delimited modifier can be applied on its own */ |
293 | 0 | while (p) { |
294 | 0 | char *next = _oidc_strstr(p, OIDC_SESSION_TYPE_SEPARATOR); |
295 | 0 | if (next) { |
296 | 0 | *next = '\0'; |
297 | 0 | next++; |
298 | 0 | } |
299 | 0 | if (_oidc_strcmp(p, OIDC_SESSION_TYPE_PERSISTENT) == 0) |
300 | 0 | cfg->persistent_session_cookie = 1; |
301 | 0 | else if (_oidc_strcmp(p, OIDC_SESSION_TYPE_STORE_ID_TOKEN) == 0) |
302 | | // only for client-cookie |
303 | 0 | cfg->store_id_token = 1; |
304 | 0 | p = next; |
305 | 0 | } |
306 | |
|
307 | 0 | return NULL; |
308 | 0 | } |
309 | | |
310 | | #define OIDC_DEFAULT_SESSION_TYPE OIDC_SESSION_TYPE_SERVER_CACHE |
311 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(session_type, int, OIDC_DEFAULT_SESSION_TYPE) |
312 | | |
313 | | #define OIDC_DEFAULT_PERSISTENT_SESSION_COOKIE 0 |
314 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(persistent_session_cookie, int, OIDC_DEFAULT_PERSISTENT_SESSION_COOKIE) |
315 | | |
316 | | #define OIDC_DEFAULT_STORE_ID_TOKEN 1 |
317 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(store_id_token, int, OIDC_DEFAULT_STORE_ID_TOKEN) |
318 | | |
319 | | static const char *oidc_valid_endpoint_auth_method_impl(apr_pool_t *pool, const char *arg, apr_byte_t has_private_key, |
320 | 12.4k | apr_byte_t allow_mtls) { |
321 | | /* Build per call because private-key validation changes the candidate list. */ |
322 | 12.4k | const char *options[9]; |
323 | 12.4k | int i = 0; |
324 | 12.4k | options[i++] = OIDC_ENDPOINT_AUTH_CLIENT_SECRET_POST; |
325 | 12.4k | options[i++] = OIDC_ENDPOINT_AUTH_CLIENT_SECRET_BASIC; |
326 | 12.4k | options[i++] = OIDC_ENDPOINT_AUTH_CLIENT_SECRET_JWT; |
327 | 12.4k | options[i++] = OIDC_ENDPOINT_AUTH_NONE; |
328 | 12.4k | options[i++] = OIDC_ENDPOINT_AUTH_BEARER_ACCESS_TOKEN; |
329 | 12.4k | if (has_private_key) |
330 | 12.4k | options[i++] = OIDC_ENDPOINT_AUTH_PRIVATE_KEY_JWT; |
331 | 12.4k | if (allow_mtls) { |
332 | 5.90k | options[i++] = OIDC_ENDPOINT_AUTH_TLS_CLIENT_AUTH; |
333 | 5.90k | options[i++] = OIDC_ENDPOINT_AUTH_SELF_SIGNED_TLS_CLIENT_AUTH; |
334 | 5.90k | } |
335 | 12.4k | options[i] = NULL; |
336 | 12.4k | return oidc_cfg_parse_is_valid_option(pool, arg, options); |
337 | 12.4k | } |
338 | | |
339 | 401 | static const char *oidc_valid_private_key_jwt_alg(apr_pool_t *pool, const char *arg) { |
340 | 401 | static const char *options[] = {"RS256", "RS384", "RS512", "PS256", "PS384", "PS512", |
341 | 401 | #if (OIDC_JOSE_EC_SUPPORT) |
342 | 401 | "ES256", "ES384", "ES512", |
343 | 401 | #endif |
344 | | |
345 | 401 | NULL}; |
346 | 401 | return oidc_cfg_parse_is_valid_option(pool, arg, options); |
347 | 401 | } |
348 | | |
349 | 12.6k | #define OIDC_ENDPOINT_AUTH_METHOD_SEPARATOR ":" |
350 | | |
351 | 12.6k | static void oidc_cfg_endpoint_auth_parse(apr_pool_t *pool, const char *arg1, char **method, char **suffix) { |
352 | 12.6k | const char *arg = apr_pstrdup(pool, arg1); |
353 | 12.6k | char *sep = _oidc_strstr(arg, OIDC_ENDPOINT_AUTH_METHOD_SEPARATOR); |
354 | 12.6k | if (sep) { |
355 | 629 | *sep = '\0'; |
356 | 629 | *suffix = apr_pstrdup(pool, ++sep); |
357 | 629 | } |
358 | 12.6k | *method = apr_pstrdup(pool, arg); |
359 | 12.6k | } |
360 | | |
361 | 12.6k | static const char *oidc_cfg_valid_endpoint_auth_method_alg(apr_pool_t *pool, const char *arg1, apr_byte_t allow_mtls) { |
362 | 12.6k | const char *rv = NULL; |
363 | 12.6k | char *method = NULL; |
364 | 12.6k | char *alg = NULL; |
365 | 12.6k | oidc_cfg_endpoint_auth_parse(pool, arg1, &method, &alg); |
366 | 12.6k | if ((_oidc_strcmp(method, OIDC_ENDPOINT_AUTH_PRIVATE_KEY_JWT) == 0) && (alg != NULL)) { |
367 | 401 | rv = oidc_valid_private_key_jwt_alg(pool, alg); |
368 | 401 | if (rv != NULL) |
369 | 204 | return rv; |
370 | 401 | } |
371 | 12.4k | return oidc_valid_endpoint_auth_method_impl(pool, method, TRUE, allow_mtls); |
372 | 12.6k | } |
373 | | |
374 | 5.99k | static const char *oidc_cfg_valid_endpoint_auth_method_with_private_key(apr_pool_t *pool, const char *arg1) { |
375 | 5.99k | return oidc_cfg_valid_endpoint_auth_method_alg(pool, arg1, TRUE); |
376 | 5.99k | } |
377 | | |
378 | 0 | static const char *oidc_cfg_valid_endpoint_auth_method_no_private_key(apr_pool_t *pool, const char *arg) { |
379 | 0 | return oidc_valid_endpoint_auth_method_impl(pool, arg, FALSE, TRUE); |
380 | 0 | } |
381 | | |
382 | 6.64k | static const char *oidc_cfg_valid_endpoint_auth_method_no_mtls_with_private_key(apr_pool_t *pool, const char *arg1) { |
383 | 6.64k | return oidc_cfg_valid_endpoint_auth_method_alg(pool, arg1, FALSE); |
384 | 6.64k | } |
385 | | |
386 | 0 | static const char *oidc_cfg_valid_endpoint_auth_method_no_mtls_no_private_key(apr_pool_t *pool, const char *arg) { |
387 | 0 | return oidc_valid_endpoint_auth_method_impl(pool, arg, FALSE, FALSE); |
388 | 0 | } |
389 | | |
390 | | const char *oidc_cfg_endpoint_auth_set(apr_pool_t *pool, const oidc_cfg_t *cfg, const char *arg1, char **auth, |
391 | 4.75k | char **alg) { |
392 | 4.75k | const char *rv = oidc_cfg_get_valid_endpoint_auth_function(cfg, TRUE)(pool, arg1); |
393 | 4.75k | if (rv == NULL) |
394 | 49 | oidc_cfg_endpoint_auth_parse(pool, arg1, auth, alg); |
395 | 4.75k | return rv; |
396 | 4.75k | } |
397 | | |
398 | | /* |
399 | | * check if the endpoint authentication method is one of the RFC 8705 mutual-TLS methods |
400 | | */ |
401 | 4.87k | apr_byte_t oidc_cfg_endpoint_auth_is_mtls(const char *method) { |
402 | 4.87k | return (_oidc_strcmp(method, OIDC_ENDPOINT_AUTH_TLS_CLIENT_AUTH) == 0) || |
403 | 4.87k | (_oidc_strcmp(method, OIDC_ENDPOINT_AUTH_SELF_SIGNED_TLS_CLIENT_AUTH) == 0); |
404 | 4.87k | } |
405 | | |
406 | | /* Select endpoint-auth validation based on private-key availability and whether mTLS is allowed. */ |
407 | 9.48k | oidc_valid_function_t oidc_cfg_get_valid_endpoint_auth_function(const oidc_cfg_t *cfg, apr_byte_t allow_mtls) { |
408 | 9.48k | if (allow_mtls == FALSE) |
409 | 4.69k | return (cfg->private_keys != NULL) ? &oidc_cfg_valid_endpoint_auth_method_no_mtls_with_private_key |
410 | 4.69k | : &oidc_cfg_valid_endpoint_auth_method_no_mtls_no_private_key; |
411 | 4.79k | return (cfg->private_keys != NULL) ? &oidc_cfg_valid_endpoint_auth_method_with_private_key |
412 | 4.79k | : &oidc_cfg_valid_endpoint_auth_method_no_private_key; |
413 | 9.48k | } |
414 | | |
415 | | #define OIDC_SESSION_INACTIVITY_TIMEOUT_MIN 10 |
416 | | #define OIDC_SESSION_INACTIVITY_TIMEOUT_MAX (3600 * 24 * 365) |
417 | | #define OIDC_DEFAULT_SESSION_INACTIVITY_TIMEOUT 300 |
418 | | |
419 | 0 | OIDC_CFG_MEMBER_FUNCS_INT(session_inactivity_timeout, OIDC_SESSION_INACTIVITY_TIMEOUT_MIN, |
420 | 0 | OIDC_SESSION_INACTIVITY_TIMEOUT_MAX, OIDC_DEFAULT_SESSION_INACTIVITY_TIMEOUT) |
421 | 0 |
|
422 | 0 | const char *oidc_cmd_public_keys_set(cmd_parms *cmd, void *ptr, const char *arg) { |
423 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
424 | 0 | const char *rv = oidc_cfg_parse_public_key_files(cmd->pool, arg, &cfg->public_keys); |
425 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
426 | 0 | } |
427 | | |
428 | | OIDC_CFG_MEMBER_FUNC_GET(public_keys, const apr_array_header_t *) |
429 | | |
430 | | /* |
431 | | * add a private key from an RSA/EC private key file to our list of JWKs with private keys |
432 | | */ |
433 | 0 | const char *oidc_cmd_private_keys_set(cmd_parms *cmd, void *ptr, const char *arg) { |
434 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
435 | 0 | const char *rv = oidc_cfg_parse_private_key_files(cmd->pool, arg, &cfg->private_keys); |
436 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
437 | 0 | } |
438 | | |
439 | | OIDC_CFG_MEMBER_FUNC_GET(private_keys, const apr_array_header_t *) |
440 | | |
441 | 0 | const char *oidc_cmd_remote_user_claim_set(cmd_parms *cmd, void *ptr, const char *v1, const char *v2, const char *v3) { |
442 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
443 | 0 | const char *rv = oidc_parse_remote_user_claim(cmd->pool, v1, v2, v3, &cfg->remote_user_claim); |
444 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
445 | 0 | } |
446 | | |
447 | 1.98k | const oidc_remote_user_claim_t *oidc_cfg_remote_user_claim_get(const oidc_cfg_t *cfg) { |
448 | 1.98k | return &cfg->remote_user_claim; |
449 | 1.98k | } |
450 | | |
451 | 990 | #define OIDC_DEFAULT_CLAIM_REMOTE_USER "sub@" |
452 | | |
453 | 990 | const char *oidc_cfg_remote_user_claim_name_get(const oidc_cfg_t *cfg) { |
454 | 990 | return cfg->remote_user_claim.claim_name != NULL ? cfg->remote_user_claim.claim_name |
455 | 990 | : OIDC_DEFAULT_CLAIM_REMOTE_USER; |
456 | 990 | } |
457 | | |
458 | | #ifdef USE_LIBJQ |
459 | | |
460 | | const char *oidc_cmd_filter_claims_expr_set(cmd_parms *cmd, void *m, const char *arg) { |
461 | | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
462 | | const char *rv = oidc_util_apr_expr_parse(cmd, arg, &cfg->filter_claims_expr, OIDC_APR_EXPR_RESULT_STRING); |
463 | | return OIDC_CONFIG_DIR_RV(cmd, rv); |
464 | | } |
465 | | |
466 | | #endif |
467 | | |
468 | | OIDC_CFG_MEMBER_FUNC_GET(filter_claims_expr, oidc_apr_expr_t *) |
469 | | |
470 | | /* |
471 | | * define which data will be returned from the info hook |
472 | | */ |
473 | 0 | const char *oidc_cmd_info_hook_data_set(cmd_parms *cmd, void *m, const char *arg) { |
474 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
475 | 0 | static const char *options[] = { |
476 | 0 | OIDC_HOOK_INFO_TIMESTAMP, OIDC_HOOK_INFO_ACCES_TOKEN, OIDC_HOOK_INFO_ACCES_TOKEN_EXP, |
477 | 0 | OIDC_HOOK_INFO_ID_TOKEN_HINT, OIDC_HOOK_INFO_ID_TOKEN, OIDC_HOOK_INFO_USER_INFO, |
478 | 0 | OIDC_HOOK_INFO_REFRESH_TOKEN, OIDC_HOOK_INFO_SESSION_EXP, OIDC_HOOK_INFO_SESSION_TIMEOUT, |
479 | 0 | OIDC_HOOK_INFO_SESSION_REMOTE_USER, OIDC_HOOK_INFO_SESSION, NULL}; |
480 | 0 | const char *rv = oidc_cfg_parse_is_valid_option(cmd->pool, arg, options); |
481 | 0 | if (rv != NULL) |
482 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
483 | 0 | if (cfg->info_hook_data == NULL) |
484 | 0 | cfg->info_hook_data = apr_hash_make(cmd->pool); |
485 | 0 | apr_hash_set(cfg->info_hook_data, arg, APR_HASH_KEY_STRING, arg); |
486 | 0 | return NULL; |
487 | 0 | } |
488 | | |
489 | | OIDC_CFG_MEMBER_FUNC_GET(info_hook_data, apr_hash_t *) |
490 | | |
491 | 0 | const char *oidc_cmd_metrics_hook_data_set(cmd_parms *cmd, void *m, const char *arg) { |
492 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
493 | 0 | const char *rv = NULL; |
494 | 0 | char *valid_names = NULL; |
495 | 0 | if (oidc_metrics_is_valid_classname(cmd->pool, arg, &valid_names) == TRUE) { |
496 | 0 | if (cfg->metrics_hook_data == NULL) |
497 | 0 | cfg->metrics_hook_data = apr_hash_make(cmd->pool); |
498 | 0 | apr_hash_set(cfg->metrics_hook_data, arg, APR_HASH_KEY_STRING, arg); |
499 | 0 | } else { |
500 | 0 | rv = apr_psprintf(cmd->pool, "undefined metric class name: \"%s\", must be one of [%s]", arg, |
501 | 0 | valid_names); |
502 | 0 | } |
503 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
504 | 0 | } |
505 | | |
506 | | OIDC_CFG_MEMBER_FUNC_GET(metrics_hook_data, apr_hash_t *) |
507 | | |
508 | 0 | #define OIDC_TRACE_PARENT_OFF_STR "off" |
509 | 0 | #define OIDC_TRACE_PARENT_PROPAGATE_STR "propagate" |
510 | 0 | #define OIDC_TRACE_PARENT_GENERATE_STR "generate" |
511 | | |
512 | 0 | const char *oidc_cmd_trace_parent_set(cmd_parms *cmd, void *struct_ptr, const char *arg) { |
513 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
514 | 0 | static const oidc_cfg_option_t options[] = {{OIDC_TRACE_PARENT_OFF, OIDC_TRACE_PARENT_OFF_STR}, |
515 | 0 | {OIDC_TRACE_PARENT_PROPAGATE, OIDC_TRACE_PARENT_PROPAGATE_STR}, |
516 | 0 | {OIDC_TRACE_PARENT_GENERATE, OIDC_TRACE_PARENT_GENERATE_STR}}; |
517 | 0 | const char *rv = |
518 | 0 | oidc_cfg_parse_option(cmd->pool, options, OIDC_CFG_OPTIONS_SIZE(options), arg, &cfg->trace_parent); |
519 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
520 | 0 | } |
521 | | |
522 | | #define OIDC_DEFAULT_TRACE_PARENT OIDC_TRACE_PARENT_OFF |
523 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(trace_parent, oidc_trace_parent_t, OIDC_DEFAULT_TRACE_PARENT) |
524 | | |
525 | | #define OIDC_DEFAULT_DPOP_API_ENABLED 0 |
526 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(dpop_api_enabled, int, OIDC_DEFAULT_DPOP_API_ENABLED) |
527 | | |
528 | 0 | const char *oidc_cmd_claim_prefix_set(cmd_parms *cmd, void *struct_ptr, const char *args) { |
529 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
530 | 0 | char *w = ap_getword_conf(cmd->pool, &args); |
531 | 0 | if (*w == '\0' || *args != 0) |
532 | 0 | cfg->claim_prefix = ""; |
533 | 0 | else |
534 | 0 | cfg->claim_prefix = w; |
535 | 0 | return NULL; |
536 | 0 | } |
537 | | |
538 | 5.51k | #define OIDC_DEFAULT_CLAIM_PREFIX "OIDC_CLAIM_" |
539 | | |
540 | 2.75k | const char *oidc_cfg_claim_prefix_get(const oidc_cfg_t *cfg) { |
541 | 2.75k | return (cfg->claim_prefix != NULL) ? cfg->claim_prefix : OIDC_DEFAULT_CLAIM_PREFIX; |
542 | 2.75k | } |
543 | | |
544 | 0 | #define OIDC_MAX_NUMBER_OF_STATE_COOKIES_MIN 0 |
545 | 0 | #define OIDC_MAX_NUMBER_OF_STATE_COOKIES_MAX 255 |
546 | | |
547 | | const char *oidc_cmd_max_number_of_state_cookies_set(cmd_parms *cmd, void *struct_ptr, const char *arg1, |
548 | 0 | const char *arg2) { |
549 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
550 | 0 | const char *rv = |
551 | 0 | oidc_cfg_parse_int_min_max(cmd->pool, arg1, &cfg->max_number_of_state_cookies, |
552 | 0 | OIDC_MAX_NUMBER_OF_STATE_COOKIES_MIN, OIDC_MAX_NUMBER_OF_STATE_COOKIES_MAX); |
553 | 0 | if ((rv == NULL) && (arg2 != NULL)) |
554 | 0 | rv = oidc_cfg_parse_boolean(cmd->pool, arg2, &cfg->delete_oldest_state_cookies); |
555 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
556 | 0 | } |
557 | | |
558 | | #define OIDC_DEFAULT_MAX_NUMBER_OF_STATE_COOKIES 7 |
559 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(max_number_of_state_cookies, int, OIDC_DEFAULT_MAX_NUMBER_OF_STATE_COOKIES) |
560 | | |
561 | | #define OIDC_DEFAULT_DELETE_OLDEST_STATE_COOKIES 0 |
562 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(delete_oldest_state_cookies, int, OIDC_DEFAULT_DELETE_OLDEST_STATE_COOKIES) |
563 | | |
564 | 0 | const char *oidc_cmd_x_forwarded_headers_set(cmd_parms *cmd, void *m, const char *arg) { |
565 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
566 | 0 | static const oidc_cfg_option_t options[] = {{OIDC_HDR_NONE, "none"}, |
567 | 0 | {OIDC_HDR_X_FORWARDED_HOST, OIDC_HTTP_HDR_X_FORWARDED_HOST}, |
568 | 0 | {OIDC_HDR_X_FORWARDED_PORT, OIDC_HTTP_HDR_X_FORWARDED_PORT}, |
569 | 0 | {OIDC_HDR_X_FORWARDED_PROTO, OIDC_HTTP_HDR_X_FORWARDED_PROTO}, |
570 | 0 | {OIDC_HDR_FORWARDED, OIDC_HTTP_HDR_FORWARDED}}; |
571 | 0 | int v = OIDC_CONFIG_POS_INT_UNSET; |
572 | 0 | const char *rv = oidc_cfg_parse_option(cmd->pool, options, OIDC_CFG_OPTIONS_SIZE(options), arg, &v); |
573 | 0 | if ((rv == NULL) && (v != OIDC_CONFIG_POS_INT_UNSET)) { |
574 | | // NB: cannot use |= with UNSET/-1 ! |
575 | 0 | if (cfg->x_forwarded_headers == OIDC_CONFIG_POS_INT_UNSET) |
576 | 0 | cfg->x_forwarded_headers = OIDC_HDR_NONE; |
577 | 0 | cfg->x_forwarded_headers |= v; |
578 | 0 | } |
579 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
580 | 0 | } |
581 | | |
582 | | #define OIDC_DEFAULT_X_FORWARDED_HEADERS OIDC_HDR_NONE |
583 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(x_forwarded_headers, oidc_hdr_x_forwarded_t, OIDC_DEFAULT_X_FORWARDED_HEADERS) |
584 | | |
585 | 12.1k | #define OIDC_CHECK_X_FORWARDED_HDR_LOG_DISABLE "OIDC_CHECK_X_FORWARDED_HDR_LOG_DISABLE" |
586 | | |
587 | | static void oidc_check_x_forwarded_hdr(request_rec *r, const oidc_hdr_x_forwarded_t x_forwarded_headers, |
588 | | const oidc_hdr_x_forwarded_t hdr_type, const char *hdr_str, |
589 | 12.1k | const char *(hdr_func)(const request_rec *r)) { |
590 | 12.1k | apr_byte_t suppress = oidc_util_spaced_string_contains( |
591 | 12.1k | r->pool, apr_table_get(r->subprocess_env, OIDC_CHECK_X_FORWARDED_HDR_LOG_DISABLE), hdr_str); |
592 | 12.1k | if (hdr_func(r)) { |
593 | 1.11k | if (!(x_forwarded_headers & hdr_type) && !suppress) |
594 | 1.11k | oidc_warn(r, "header %s received but %s not configured for it", hdr_str, OIDCXForwardedHeaders); |
595 | 11.0k | } else { |
596 | 11.0k | if ((x_forwarded_headers & hdr_type) && !suppress) |
597 | 11.0k | oidc_warn(r, "%s configured for header %s but not found in request", OIDCXForwardedHeaders, |
598 | 11.0k | hdr_str); |
599 | 11.0k | } |
600 | 12.1k | } |
601 | | |
602 | 3.04k | void oidc_cfg_x_forwarded_headers_check(request_rec *r, oidc_hdr_x_forwarded_t x_forwarded_headers) { |
603 | 3.04k | oidc_check_x_forwarded_hdr(r, x_forwarded_headers, OIDC_HDR_X_FORWARDED_HOST, OIDC_HTTP_HDR_X_FORWARDED_HOST, |
604 | 3.04k | oidc_http_hdr_in_x_forwarded_host_get); |
605 | 3.04k | oidc_check_x_forwarded_hdr(r, x_forwarded_headers, OIDC_HDR_X_FORWARDED_PORT, OIDC_HTTP_HDR_X_FORWARDED_PORT, |
606 | 3.04k | oidc_http_hdr_in_x_forwarded_port_get); |
607 | 3.04k | oidc_check_x_forwarded_hdr(r, x_forwarded_headers, OIDC_HDR_X_FORWARDED_PROTO, OIDC_HTTP_HDR_X_FORWARDED_PROTO, |
608 | 3.04k | oidc_http_hdr_in_x_forwarded_proto_get); |
609 | 3.04k | oidc_check_x_forwarded_hdr(r, x_forwarded_headers, OIDC_HDR_FORWARDED, OIDC_HTTP_HDR_FORWARDED, |
610 | 3.04k | oidc_http_hdr_in_forwarded_get); |
611 | 3.04k | } |
612 | | |
613 | 0 | #define OIDC_STATE_INPUT_HEADERS_AS_NONE "none" |
614 | 0 | #define OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT "user-agent" |
615 | 0 | #define OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR "x-forwarded-for" |
616 | 0 | #define OIDC_STATE_INPUT_HEADERS_AS_BOTH "both" |
617 | | |
618 | | /* |
619 | | * define which header we use for calculating the fingerprint of the state during authentication |
620 | | */ |
621 | 0 | const char *oidc_cmd_state_input_headers_set(cmd_parms *cmd, void *m, const char *arg) { |
622 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
623 | 0 | static const oidc_cfg_option_t options[] = { |
624 | 0 | {OIDC_STATE_INPUT_HEADERS_NONE, OIDC_STATE_INPUT_HEADERS_AS_NONE}, |
625 | 0 | {OIDC_STATE_INPUT_HEADERS_USER_AGENT, OIDC_STATE_INPUT_HEADERS_AS_USER_AGENT}, |
626 | 0 | {OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR, OIDC_STATE_INPUT_HEADERS_AS_X_FORWARDED_FOR}, |
627 | 0 | {OIDC_STATE_INPUT_HEADERS_USER_AGENT | OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR, |
628 | 0 | OIDC_STATE_INPUT_HEADERS_AS_BOTH}}; |
629 | 0 | const char *rv = |
630 | 0 | oidc_cfg_parse_option(cmd->pool, options, OIDC_CFG_OPTIONS_SIZE(options), arg, &cfg->state_input_headers); |
631 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
632 | 0 | } |
633 | | |
634 | | #define OIDC_DEFAULT_STATE_INPUT_HEADERS OIDC_STATE_INPUT_HEADERS_USER_AGENT |
635 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(state_input_headers, oidc_state_input_hdrs_t, OIDC_DEFAULT_STATE_INPUT_HEADERS) |
636 | | |
637 | 0 | const char *oidc_cmd_post_preserve_templates_set(cmd_parms *cmd, void *m, const char *arg1, const char *arg2) { |
638 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
639 | 0 | const char *rv = NULL; |
640 | 0 | if (arg1 != NULL) |
641 | 0 | rv = oidc_cfg_parse_filename(cmd->pool, arg1, &cfg->post_preserve_template); |
642 | 0 | if ((rv == NULL) && (arg2 != NULL)) |
643 | 0 | rv = oidc_cfg_parse_filename(cmd->pool, arg2, &cfg->post_restore_template); |
644 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
645 | 0 | } |
646 | | |
647 | | OIDC_CFG_MEMBER_FUNC_GET(post_preserve_template, const char *) |
648 | | OIDC_CFG_MEMBER_FUNC_GET(post_restore_template, const char *) |
649 | | |
650 | 0 | const char *oidc_cmd_ca_bundle_path_set(cmd_parms *cmd, void *ptr, const char *arg) { |
651 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
652 | 0 | const char *rv = oidc_cfg_parse_filename(cmd->pool, arg, &cfg->ca_bundle_path); |
653 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
654 | 0 | } |
655 | | |
656 | | OIDC_CFG_MEMBER_FUNC_GET(ca_bundle_path, const char *) |
657 | | |
658 | 0 | const char *oidc_cmd_metadata_dir_set(cmd_parms *cmd, void *ptr, const char *arg) { |
659 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
660 | 0 | const char *rv = oidc_cfg_parse_dirname(cmd->pool, arg, &cfg->metadata_dir); |
661 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
662 | 0 | } |
663 | | |
664 | | OIDC_CFG_MEMBER_FUNC_GET(metadata_dir, const char *) |
665 | | |
666 | | #define OIDC_DEFAULT_COOKIE_HTTPONLY 1 |
667 | 0 | OIDC_CFG_MEMBER_FUNCS_BOOL(cookie_http_only, OIDC_DEFAULT_COOKIE_HTTPONLY) |
668 | 0 |
|
669 | 0 | #define OIDC_SAMESITE_COOKIE_OFF_STR "Off" |
670 | 0 | #define OIDC_SAMESITE_COOKIE_ON_STR "On" |
671 | 0 | #define OIDC_SAMESITE_COOKIE_DISABLED_STR "Disabled" |
672 | 0 | #define OIDC_SAMESITE_COOKIE_NONE_STR "None" |
673 | 0 | #define OIDC_SAMESITE_COOKIE_LAX_STR "Lax" |
674 | 0 | #define OIDC_SAMESITE_COOKIE_STRICT_STR "Strict" |
675 | | |
676 | | /* |
677 | | * define which header we use for calculating the fingerprint of the state during authentication |
678 | | */ |
679 | | const char *oidc_cmd_cookie_same_site_session_set(cmd_parms *cmd, void *m, const char *arg1, const char *arg2, |
680 | 0 | const char *arg3) { |
681 | 0 | oidc_cfg_t *cfg = (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); |
682 | | // NB: On is made equal to Lax here and Off is equal to None (backwards compatibility) |
683 | 0 | static const oidc_cfg_option_t options[] = {{OIDC_SAMESITE_COOKIE_NONE, OIDC_SAMESITE_COOKIE_OFF_STR}, |
684 | 0 | {OIDC_SAMESITE_COOKIE_LAX, OIDC_SAMESITE_COOKIE_ON_STR}, |
685 | 0 | {OIDC_SAMESITE_COOKIE_DISABLED, OIDC_SAMESITE_COOKIE_DISABLED_STR}, |
686 | 0 | {OIDC_SAMESITE_COOKIE_NONE, OIDC_SAMESITE_COOKIE_NONE_STR}, |
687 | 0 | {OIDC_SAMESITE_COOKIE_LAX, OIDC_SAMESITE_COOKIE_LAX_STR}, |
688 | 0 | {OIDC_SAMESITE_COOKIE_STRICT, OIDC_SAMESITE_COOKIE_STRICT_STR}}; |
689 | 0 | const char *rv = oidc_cfg_parse_option_ignore_case(cmd->pool, options, OIDC_CFG_OPTIONS_SIZE(options), arg1, |
690 | 0 | &cfg->cookie_same_site_session); |
691 | 0 | if ((rv == NULL) && (arg2 != NULL)) { |
692 | 0 | static const oidc_cfg_option_t state_options[] = { |
693 | 0 | {OIDC_SAMESITE_COOKIE_DISABLED, OIDC_SAMESITE_COOKIE_DISABLED_STR}, |
694 | 0 | {OIDC_SAMESITE_COOKIE_NONE, OIDC_SAMESITE_COOKIE_NONE_STR}, |
695 | 0 | {OIDC_SAMESITE_COOKIE_LAX, OIDC_SAMESITE_COOKIE_LAX_STR}}; |
696 | 0 | rv = oidc_cfg_parse_option_ignore_case(cmd->pool, state_options, OIDC_CFG_OPTIONS_SIZE(state_options), |
697 | 0 | arg2, &cfg->cookie_same_site_state); |
698 | 0 | } |
699 | 0 | if ((rv == NULL) && (arg3 != NULL)) { |
700 | 0 | static const oidc_cfg_option_t csrf_options[] = { |
701 | 0 | {OIDC_SAMESITE_COOKIE_DISABLED, OIDC_SAMESITE_COOKIE_DISABLED_STR}, |
702 | 0 | {OIDC_SAMESITE_COOKIE_NONE, OIDC_SAMESITE_COOKIE_NONE_STR}, |
703 | 0 | {OIDC_SAMESITE_COOKIE_LAX, OIDC_SAMESITE_COOKIE_LAX_STR}, |
704 | 0 | {OIDC_SAMESITE_COOKIE_STRICT, OIDC_SAMESITE_COOKIE_STRICT_STR}}; |
705 | 0 | rv = oidc_cfg_parse_option_ignore_case(cmd->pool, csrf_options, OIDC_CFG_OPTIONS_SIZE(csrf_options), |
706 | 0 | arg3, &cfg->cookie_same_site_discovery_csrf); |
707 | 0 | } |
708 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); |
709 | 0 | } |
710 | | |
711 | | #define OIDC_DEFAULT_COOKIE_SAME_SITE OIDC_SAMESITE_COOKIE_LAX |
712 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(cookie_same_site_session, oidc_samesite_cookie_t, OIDC_DEFAULT_COOKIE_SAME_SITE) |
713 | | |
714 | | #define OIDC_DEFAULT_COOKIE_SAME_SITE_STATE oidc_cfg_cookie_same_site_session_get(cfg) |
715 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(cookie_same_site_state, oidc_samesite_cookie_t, OIDC_DEFAULT_COOKIE_SAME_SITE_STATE) |
716 | | |
717 | | #define OIDC_DEFAULT_COOKIE_SAME_SITE_CSRF_DISCOVERY oidc_cfg_cookie_same_site_session_get(cfg) |
718 | | OIDC_CFG_MEMBER_FUNC_TYPE_GET(cookie_same_site_discovery_csrf, oidc_samesite_cookie_t, |
719 | | OIDC_DEFAULT_COOKIE_SAME_SITE_CSRF_DISCOVERY) |
720 | | |
721 | | #define OIDC_DEFAULT_SESSION_FALLBACK_TO_COOKIE 0 |
722 | 0 | OIDC_CFG_MEMBER_FUNCS_BOOL(session_cache_fallback_to_cookie, OIDC_DEFAULT_SESSION_FALLBACK_TO_COOKIE) |
723 | 0 |
|
724 | 0 | /* masking secrets in the debug log is the default; turning it off is a deliberate, logged choice */ |
725 | 0 | #define OIDC_DEFAULT_DEBUG_MASK_SECRETS 1 |
726 | 0 | OIDC_CFG_MEMBER_FUNCS_BOOL(debug_mask_secrets, OIDC_DEFAULT_DEBUG_MASK_SECRETS) |
727 | 0 |
|
728 | 0 | #define OIDC_DEFAULT_CLAIM_DELIMITER "," |
729 | 1.13k | OIDC_CFG_MEMBER_FUNCS_STR_DEF(claim_delimiter, NULL, OIDC_DEFAULT_CLAIM_DELIMITER) Unexecuted instantiation: oidc_cmd_claim_delimiter_set oidc_cfg_claim_delimiter_get Line | Count | Source | 729 | | OIDC_CFG_MEMBER_FUNCS_STR_DEF(claim_delimiter, NULL, OIDC_DEFAULT_CLAIM_DELIMITER) |
|
730 | | |
731 | 150 | OIDC_CFG_MEMBER_FUNCS_STR_DEF(metrics_path, NULL, NULL) Unexecuted instantiation: oidc_cmd_metrics_path_set oidc_cfg_metrics_path_get Line | Count | Source | 731 | | OIDC_CFG_MEMBER_FUNCS_STR_DEF(metrics_path, NULL, NULL) |
|
732 | | |
733 | | #define OIDC_DEFAULT_LOGOUT_X_FRAME_OPTIONS "DENY" |
734 | 23 | OIDC_CFG_MEMBER_FUNCS_STR_DEF(logout_x_frame_options, NULL, OIDC_DEFAULT_LOGOUT_X_FRAME_OPTIONS) Unexecuted instantiation: oidc_cmd_logout_x_frame_options_set oidc_cfg_logout_x_frame_options_get Line | Count | Source | 734 | | OIDC_CFG_MEMBER_FUNCS_STR_DEF(logout_x_frame_options, NULL, OIDC_DEFAULT_LOGOUT_X_FRAME_OPTIONS) |
|
735 | | |
736 | | #define OIDC_STATE_TIMEOUT_MIN 1 |
737 | | #define OIDC_STATE_TIMEOUT_MAX (3600 * 24 * 30) |
738 | | #define OIDC_DEFAULT_STATE_TIMEOUT 300 |
739 | | |
740 | 0 | OIDC_CFG_MEMBER_FUNCS_INT(state_timeout, OIDC_STATE_TIMEOUT_MIN, OIDC_STATE_TIMEOUT_MAX, OIDC_DEFAULT_STATE_TIMEOUT) |
741 | 0 |
|
742 | 0 | #define OIDC_SESSION_CLIENT_COOKIE_CHUNK_SIZE_MIN 256 |
743 | 0 | #define OIDC_SESSION_CLIENT_COOKIE_CHUNK_SIZE_MAX (1024 * 64) |
744 | 0 | #define OIDC_DEFAULT_SESSION_CLIENT_COOKIE_CHUNK_SIZE 4000 |
745 | 0 |
|
746 | 0 | /* NB: 0 is accepted on top of the range and means "never chunk", which is what the |
747 | 0 | * runtime already implements in oidc_http_set_chunked_cookie() */ |
748 | 0 | OIDC_CFG_MEMBER_FUNCS_INT_EXT(session_cookie_chunk_size, |
749 | | oidc_cfg_parse_int_min_max_or_zero(cmd->pool, arg, &v, |
750 | | OIDC_SESSION_CLIENT_COOKIE_CHUNK_SIZE_MIN, |
751 | | OIDC_SESSION_CLIENT_COOKIE_CHUNK_SIZE_MAX), |
752 | | OIDC_DEFAULT_SESSION_CLIENT_COOKIE_CHUNK_SIZE) |
753 | | |
754 | | #define OIDC_PROVIDER_METADATA_REFRESH_INTERVAL_MIN 30 |
755 | | #define OIDC_PROVIDER_METADATA_REFRESH_INTERVAL_MAX (3600 * 24 * 365) |
756 | | #define OIDC_DEFAULT_PROVIDER_METADATA_REFRESH_INTERVAL 0 |
757 | | |
758 | 0 | OIDC_CFG_MEMBER_FUNCS_INT(provider_metadata_refresh_interval, OIDC_PROVIDER_METADATA_REFRESH_INTERVAL_MIN, |
759 | 0 | OIDC_PROVIDER_METADATA_REFRESH_INTERVAL_MAX, OIDC_DEFAULT_PROVIDER_METADATA_REFRESH_INTERVAL) |
760 | 0 |
|
761 | 0 | #define OIDC_CFG_MEMBER_FUNCS_HASHTABLE(member) \ |
762 | 0 | const char *oidc_cmd_##member##_set(cmd_parms *cmd, void *ptr, const char *arg) { \ |
763 | 0 | oidc_cfg_t *cfg = \ |
764 | 0 | (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); \ |
765 | 0 | if (cfg->member == NULL) \ |
766 | 0 | cfg->member = apr_hash_make(cmd->pool); \ |
767 | 0 | apr_hash_set(cfg->member, arg, APR_HASH_KEY_STRING, arg); \ |
768 | 0 | return NULL; \ |
769 | 0 | } \ |
770 | | \ |
771 | | OIDC_CFG_MEMBER_FUNC_GET(member, apr_hash_t *) |
772 | | |
773 | 0 | OIDC_CFG_MEMBER_FUNCS_HASHTABLE(white_listed_claims) |
774 | 0 | OIDC_CFG_MEMBER_FUNCS_HASHTABLE(black_listed_claims) |
775 | 0 | OIDC_CFG_MEMBER_FUNCS_HASHTABLE(redirect_urls_allowed) |
776 | 0 | OIDC_CFG_MEMBER_FUNCS_HASHTABLE(discover_issuers_allowed) |
777 | | |
778 | | #define OIDC_CFG_MEMBER_FUNCS_ABS_OR_REL_URI(member) \ |
779 | 0 | const char *oidc_cmd_##member##_set(cmd_parms *cmd, void *ptr, const char *arg) { \ |
780 | 0 | oidc_cfg_t *cfg = \ |
781 | 0 | (oidc_cfg_t *)ap_get_module_config(cmd->server->module_config, &auth_openidc_module); \ |
782 | 0 | const char *rv = oidc_cfg_parse_relative_or_absolute_url(cmd->pool, arg, &cfg->member); \ |
783 | 0 | return OIDC_CONFIG_DIR_RV(cmd, rv); \ |
784 | 0 | } \ |
785 | | \ |
786 | | OIDC_CFG_MEMBER_FUNC_GET(member, const char *) |
787 | | |
788 | 0 | OIDC_CFG_MEMBER_FUNCS_ABS_OR_REL_URI(redirect_uri) |
789 | 0 | OIDC_CFG_MEMBER_FUNCS_ABS_OR_REL_URI(default_sso_url) |
790 | 0 | OIDC_CFG_MEMBER_FUNCS_ABS_OR_REL_URI(default_slo_url) |
791 | | |
792 | | typedef struct oidc_cfg_cleanup_ctx_t { |
793 | | oidc_cfg_t *cfg; |
794 | | apr_pool_t *pool; |
795 | | server_rec *svr; |
796 | | } oidc_cfg_cleanup_ctx_t; |
797 | | |
798 | | /* |
799 | | * destroy a server config record and its members |
800 | | */ |
801 | 0 | apr_byte_t oidc_cfg_server_destroy(apr_pool_t *pool, server_rec *s, oidc_cfg_t *cfg) { |
802 | 0 | if ((cfg->cache.impl) && (cfg->cache.impl->destroy)) |
803 | 0 | cfg->cache.impl->destroy(pool, s); |
804 | 0 | cfg->cache.impl = NULL; |
805 | 0 | oidc_cfg_provider_destroy(cfg->provider); |
806 | 0 | cfg->provider = NULL; |
807 | 0 | oidc_cfg_oauth_destroy(cfg->oauth); |
808 | 0 | cfg->oauth = NULL; |
809 | 0 | oidc_jwk_list_destroy(cfg->public_keys); |
810 | 0 | cfg->public_keys = NULL; |
811 | 0 | oidc_jwk_list_destroy(cfg->private_keys); |
812 | 0 | cfg->private_keys = NULL; |
813 | 0 | return TRUE; |
814 | 0 | } |
815 | | |
816 | 0 | static apr_status_t oidc_cfg_server_cleanup(void *data) { |
817 | 0 | oidc_cfg_cleanup_ctx_t *ctx = (oidc_cfg_cleanup_ctx_t *)data; |
818 | 0 | oidc_cfg_t *cfg = ctx->cfg; |
819 | 0 | return oidc_cfg_server_destroy(ctx->pool, ctx->svr, cfg) ? APR_SUCCESS : APR_EGENERAL; |
820 | 0 | } |
821 | | |
822 | 40 | static oidc_cfg_t *oidc_cfg_server_alloc(apr_pool_t *pool, server_rec *s) { |
823 | 40 | oidc_cfg_t *c = apr_pcalloc(pool, sizeof(oidc_cfg_t)); |
824 | 40 | oidc_cfg_cleanup_ctx_t *ctx = apr_pcalloc(pool, sizeof(oidc_cfg_cleanup_ctx_t)); |
825 | 40 | ctx->cfg = c; |
826 | | // pconf pool used at destruction time |
827 | 40 | ctx->pool = pool; |
828 | 40 | ctx->svr = s; |
829 | | // register a cleanup handler on the config pool to handle graceful restarts without increasing |
830 | | // memory consumption; this MUST be a *pre*-cleanup: libapr registers its own regular cleanups |
831 | | // for the cache global mutex's POSIX semaphore and the shm segment on this same pool when they |
832 | | // are created at post_config time, and regular cleanups run in reverse order of registration, |
833 | | // so a regular registration here (which happens much earlier, at config-parse time) would run |
834 | | // only AFTER libapr has already closed/unmapped those objects, making the mutex unlock/destroy |
835 | | // in oidc_cache_shm_destroy() dereference an unmapped semaphore and segfault the parent process |
836 | | // on a graceful restart; pre-cleanups are guaranteed to run before all regular cleanups |
837 | 40 | apr_pool_pre_cleanup_register(pool, ctx, oidc_cfg_server_cleanup); |
838 | 40 | return c; |
839 | 40 | } |
840 | | |
841 | | /* |
842 | | * create a new server config record with defaults |
843 | | */ |
844 | 480 | #define OIDC_SVR_M_CREATE_PTR(type, name) c->name = NULL; |
845 | 800 | #define OIDC_SVR_M_CREATE_INT(name) c->name = OIDC_CONFIG_POS_INT_UNSET; |
846 | 0 | #define OIDC_SVR_M_MERGE_PTR(type, name) c->name = _oidc_cfg_merge_ptr(add->name, base->name); |
847 | 0 | #define OIDC_SVR_M_MERGE_INT(name) c->name = _oidc_cfg_merge_pos_int(add->name, base->name); |
848 | | |
849 | 40 | void *oidc_cfg_server_create(apr_pool_t *pool, server_rec *svr) { |
850 | 40 | oidc_cfg_t *c = oidc_cfg_server_alloc(pool, svr); |
851 | 40 | c->svr = svr; |
852 | | |
853 | 40 | c->merged = FALSE; |
854 | | /* nothing to inherit from: whatever OIDCRedirectURI this config ends up with is its own */ |
855 | 40 | c->redirect_uri_inherited = FALSE; |
856 | | |
857 | 800 | OIDC_SVR_CFG_SIMPLE_MEMBERS(OIDC_SVR_M_CREATE_PTR, OIDC_SVR_M_CREATE_INT) |
858 | 40 | c->public_keys = NULL; |
859 | 40 | c->private_keys = NULL; |
860 | | |
861 | 40 | c->provider = oidc_cfg_provider_create(pool); |
862 | 40 | c->oauth = oidc_cfg_oauth_create(pool); |
863 | 40 | oidc_cfg_cache_create_server_config(c); |
864 | | |
865 | 40 | c->http_timeout_long.request_timeout = OIDC_CONFIG_POS_INT_UNSET; |
866 | 40 | c->http_timeout_long.connect_timeout = OIDC_DEFAULT_HTTP_CONNECT_TIMEOUT_LONG; |
867 | 40 | c->http_timeout_long.retries = OIDC_DEFAULT_HTTP_RETRIES_LONG; |
868 | 40 | c->http_timeout_long.retry_interval = OIDC_DEFAULT_HTTP_RETRY_INTERVAL_LONG; |
869 | 40 | c->http_timeout_short.request_timeout = OIDC_CONFIG_POS_INT_UNSET; |
870 | 40 | c->http_timeout_short.connect_timeout = OIDC_DEFAULT_HTTP_CONNECT_TIMEOUT_SHORT; |
871 | 40 | c->http_timeout_short.retries = OIDC_DEFAULT_HTTP_RETRIES_SHORT; |
872 | 40 | c->http_timeout_short.retry_interval = OIDC_DEFAULT_HTTP_RETRY_INTERVAL_SHORT; |
873 | | |
874 | 40 | c->remote_user_claim.claim_name = NULL; |
875 | 40 | c->remote_user_claim.reg_exp = NULL; |
876 | 40 | c->remote_user_claim.replace = NULL; |
877 | | |
878 | 40 | c->outgoing_proxy.host_port = NULL; |
879 | 40 | c->outgoing_proxy.username_password = NULL; |
880 | 40 | c->outgoing_proxy.auth_type = OIDC_CONFIG_POS_INT_UNSET; |
881 | | |
882 | 40 | c->crypto_passphrase.secret1 = NULL; |
883 | 40 | c->crypto_passphrase.secret2 = NULL; |
884 | 40 | c->crypto_passphrase.derived_key1_set = FALSE; |
885 | 40 | c->crypto_passphrase.derived_key2_set = FALSE; |
886 | | |
887 | 40 | c->info_hook_data = NULL; |
888 | 40 | c->metrics_hook_data = NULL; |
889 | | |
890 | 40 | c->black_listed_claims = NULL; |
891 | 40 | c->white_listed_claims = NULL; |
892 | 40 | c->filter_claims_expr = NULL; |
893 | | |
894 | 40 | c->redirect_urls_allowed = NULL; |
895 | 40 | c->discover_issuers_allowed = NULL; |
896 | | |
897 | 40 | return c; |
898 | 40 | } |
899 | | |
900 | | /* |
901 | | * merge an http_timeout struct: "add" wins as a whole if it has been configured |
902 | | */ |
903 | | static void _oidc_cfg_merge_http_timeout(oidc_http_timeout_t *c, const oidc_http_timeout_t *add, |
904 | 0 | const oidc_http_timeout_t *base) { |
905 | 0 | const oidc_http_timeout_t *src = add->request_timeout != OIDC_CONFIG_POS_INT_UNSET ? add : base; |
906 | 0 | c->request_timeout = src->request_timeout; |
907 | 0 | c->connect_timeout = src->connect_timeout; |
908 | 0 | c->retries = src->retries; |
909 | 0 | c->retry_interval = src->retry_interval; |
910 | 0 | } |
911 | | |
912 | | /* |
913 | | * merge the remote_user_claim struct: "add" wins as a whole if it has been configured |
914 | | */ |
915 | | static void _oidc_cfg_merge_remote_user_claim(oidc_remote_user_claim_t *c, const oidc_remote_user_claim_t *add, |
916 | 0 | const oidc_remote_user_claim_t *base) { |
917 | 0 | const oidc_remote_user_claim_t *src = add->claim_name != NULL ? add : base; |
918 | 0 | c->claim_name = src->claim_name; |
919 | 0 | c->reg_exp = src->reg_exp; |
920 | 0 | c->replace = src->replace; |
921 | 0 | } |
922 | | |
923 | | /* |
924 | | * merge the outgoing_proxy struct: "add" wins as a whole if it has been configured |
925 | | */ |
926 | | static void _oidc_cfg_merge_outgoing_proxy(oidc_http_outgoing_proxy_t *c, const oidc_http_outgoing_proxy_t *add, |
927 | 0 | const oidc_http_outgoing_proxy_t *base) { |
928 | 0 | const oidc_http_outgoing_proxy_t *src = add->host_port != NULL ? add : base; |
929 | 0 | c->host_port = src->host_port; |
930 | 0 | c->username_password = src->username_password; |
931 | 0 | c->auth_type = src->auth_type; |
932 | 0 | } |
933 | | |
934 | | /* |
935 | | * merge the crypto_passphrase struct: "add" wins as a whole if it has been configured |
936 | | */ |
937 | | static void _oidc_cfg_merge_crypto_passphrase(oidc_crypto_passphrase_t *c, const oidc_crypto_passphrase_t *add, |
938 | 0 | const oidc_crypto_passphrase_t *base) { |
939 | 0 | const oidc_crypto_passphrase_t *src = add->secret1 != NULL ? add : base; |
940 | 0 | c->secret1 = src->secret1; |
941 | 0 | c->secret2 = src->secret2; |
942 | | /* Keep derived keys paired with the selected secrets, regardless of merge/derivation order. */ |
943 | 0 | c->derived_key1_set = src->derived_key1_set; |
944 | 0 | if (src->derived_key1_set) |
945 | 0 | _oidc_memcpy(c->derived_key1, src->derived_key1, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN); |
946 | 0 | c->derived_key2_set = src->derived_key2_set; |
947 | 0 | if (src->derived_key2_set) |
948 | 0 | _oidc_memcpy(c->derived_key2, src->derived_key2, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN); |
949 | 0 | } |
950 | | |
951 | | /* |
952 | | * merge a new server config with a base one |
953 | | */ |
954 | 0 | void *oidc_cfg_server_merge(apr_pool_t *pool, void *BASE, void *ADD) { |
955 | 0 | const oidc_cfg_t *base = (const oidc_cfg_t *)BASE; |
956 | 0 | const oidc_cfg_t *add = (const oidc_cfg_t *)ADD; |
957 | |
|
958 | 0 | oidc_cfg_t *c = oidc_cfg_server_alloc(pool, add->svr); |
959 | |
|
960 | 0 | c->provider = oidc_cfg_provider_create(pool); |
961 | 0 | c->oauth = oidc_cfg_oauth_create(pool); |
962 | |
|
963 | 0 | c->merged = TRUE; |
964 | | /* record where the merged redirect_uri came from while "add" (this vhost's own config) and |
965 | | * "base" can still be told apart: the merge below collapses them into a single value */ |
966 | 0 | c->redirect_uri_inherited = (add->redirect_uri == NULL) && (base->redirect_uri != NULL); |
967 | |
|
968 | 0 | oidc_cfg_provider_merge(pool, c->provider, base->provider, add->provider); |
969 | 0 | oidc_cfg_oauth_merge(pool, c->oauth, base->oauth, add->oauth); |
970 | 0 | oidc_cfg_cache_merge_server_config(c, base, add); |
971 | |
|
972 | 0 | OIDC_SVR_CFG_SIMPLE_MEMBERS(OIDC_SVR_M_MERGE_PTR, OIDC_SVR_M_MERGE_INT) |
973 | 0 | c->public_keys = oidc_jwk_list_copy(pool, _oidc_cfg_merge_ptr(add->public_keys, base->public_keys)); |
974 | 0 | c->private_keys = oidc_jwk_list_copy(pool, _oidc_cfg_merge_ptr(add->private_keys, base->private_keys)); |
975 | |
|
976 | 0 | _oidc_cfg_merge_http_timeout(&c->http_timeout_long, &add->http_timeout_long, &base->http_timeout_long); |
977 | 0 | _oidc_cfg_merge_http_timeout(&c->http_timeout_short, &add->http_timeout_short, &base->http_timeout_short); |
978 | |
|
979 | 0 | _oidc_cfg_merge_remote_user_claim(&c->remote_user_claim, &add->remote_user_claim, &base->remote_user_claim); |
980 | |
|
981 | 0 | _oidc_cfg_merge_outgoing_proxy(&c->outgoing_proxy, &add->outgoing_proxy, &base->outgoing_proxy); |
982 | 0 | _oidc_cfg_merge_crypto_passphrase(&c->crypto_passphrase, &add->crypto_passphrase, &base->crypto_passphrase); |
983 | |
|
984 | 0 | c->info_hook_data = _oidc_cfg_merge_ptr(add->info_hook_data, base->info_hook_data); |
985 | 0 | c->metrics_hook_data = _oidc_cfg_merge_ptr(add->metrics_hook_data, base->metrics_hook_data); |
986 | |
|
987 | 0 | c->black_listed_claims = _oidc_cfg_merge_ptr(add->black_listed_claims, base->black_listed_claims); |
988 | 0 | c->white_listed_claims = _oidc_cfg_merge_ptr(add->white_listed_claims, base->white_listed_claims); |
989 | 0 | c->filter_claims_expr = _oidc_cfg_merge_ptr(add->filter_claims_expr, base->filter_claims_expr); |
990 | |
|
991 | 0 | c->redirect_urls_allowed = _oidc_cfg_merge_ptr(add->redirect_urls_allowed, base->redirect_urls_allowed); |
992 | 0 | c->discover_issuers_allowed = |
993 | 0 | _oidc_cfg_merge_ptr(add->discover_issuers_allowed, base->discover_issuers_allowed); |
994 | |
|
995 | 0 | return c; |
996 | 0 | } |
997 | | |
998 | | /* |
999 | | * Build a read-only request view with a private OAuth struct, allowing metadata endpoints to |
1000 | | * change without mutating shared server state. Other members remain shared. |
1001 | | */ |
1002 | 0 | oidc_cfg_t *oidc_cfg_request_view(apr_pool_t *pool, const oidc_cfg_t *c) { |
1003 | 0 | oidc_cfg_t *rc = apr_pmemdup(pool, c, sizeof(*c)); |
1004 | 0 | rc->oauth = oidc_cfg_oauth_shallow_copy(pool, c->oauth); |
1005 | 0 | return rc; |
1006 | 0 | } |
1007 | | |
1008 | | /* |
1009 | | * initialize before the post config handler runs |
1010 | | */ |
1011 | 38 | void oidc_pre_config_init(void) { |
1012 | 38 | #if OPENSSL_API_COMPAT < 0x10100000L |
1013 | 38 | ERR_load_crypto_strings(); |
1014 | 38 | OpenSSL_add_all_algorithms(); |
1015 | 38 | OpenSSL_add_all_digests(); |
1016 | | #else |
1017 | | OPENSSL_init_crypto(0, NULL); |
1018 | | #endif |
1019 | 38 | } |
1020 | | |
1021 | 232k | oidc_provider_t *oidc_cfg_provider_get(oidc_cfg_t *cfg) { |
1022 | 232k | return cfg->provider; |
1023 | 232k | } |
1024 | | |
1025 | 0 | int oidc_cfg_merged_get(const oidc_cfg_t *cfg) { |
1026 | 0 | return cfg->merged; |
1027 | 0 | } |
1028 | | |
1029 | 0 | int oidc_cfg_redirect_uri_inherited_get(const oidc_cfg_t *cfg) { |
1030 | 0 | return cfg->redirect_uri_inherited; |
1031 | 0 | } |
1032 | | |
1033 | | static oidc_cache_mutex_t *_oidc_refresh_mutex = NULL; |
1034 | | |
1035 | 22 | oidc_cache_mutex_t *oidc_cfg_refresh_mutex_get(const oidc_cfg_t *cfg) { |
1036 | 22 | return _oidc_refresh_mutex; |
1037 | 22 | } |
1038 | | |
1039 | 38 | int oidc_cfg_post_config(apr_pool_t *pool, oidc_cfg_t *cfg, server_rec *s) { |
1040 | 38 | if (cfg->cache.impl == NULL) |
1041 | 0 | cfg->cache.impl = &oidc_cache_shm; |
1042 | 38 | if ((cfg->cache.impl->post_config != NULL) && (cfg->cache.impl->post_config(pool, s) != OK)) |
1043 | 0 | return HTTP_INTERNAL_SERVER_ERROR; |
1044 | 38 | if (_oidc_refresh_mutex == NULL) { |
1045 | | // NB: use the process pool here as the mutex is a process-wide singleton |
1046 | 38 | _oidc_refresh_mutex = oidc_cache_mutex_create(s->process->pool, TRUE); |
1047 | 38 | if (oidc_cache_mutex_post_config(s->process->pool, s, _oidc_refresh_mutex, "refresh") != TRUE) |
1048 | 0 | return HTTP_INTERNAL_SERVER_ERROR; |
1049 | 38 | } |
1050 | 38 | if ((cfg->metrics_hook_data != NULL) && (oidc_metrics_post_config(pool, s) != TRUE)) |
1051 | 0 | return HTTP_INTERNAL_SERVER_ERROR; |
1052 | 38 | return OK; |
1053 | 38 | } |
1054 | | |
1055 | 0 | void oidc_cfg_child_init(apr_pool_t *pool, const oidc_cfg_t *cfg, server_rec *s) { |
1056 | 0 | if ((cfg->cache.impl->child_init != NULL) && (cfg->cache.impl->child_init(pool, s) != APR_SUCCESS)) |
1057 | 0 | oidc_serror(s, "cfg->cache->child_init failed"); |
1058 | 0 | if ((_oidc_refresh_mutex != NULL) && (oidc_cache_mutex_child_init(pool, s, _oidc_refresh_mutex) != APR_SUCCESS)) |
1059 | 0 | oidc_serror(s, "oidc_cache_mutex_child_init on refresh mutex failed"); |
1060 | 0 | if ((cfg->metrics_hook_data != NULL) && (oidc_metrics_child_init(pool, s) != APR_SUCCESS)) |
1061 | 0 | oidc_serror(s, "oidc_metrics_cache_child_init failed"); |
1062 | 0 | } |
1063 | | |
1064 | 0 | void oidc_cfg_process_cleanup(oidc_cfg_t *cfg, server_rec *s) { |
1065 | 0 | if (_oidc_refresh_mutex != NULL) { |
1066 | 0 | if (oidc_cache_mutex_destroy(s, _oidc_refresh_mutex) != TRUE) { |
1067 | 0 | oidc_serror(s, "oidc_cache_mutex_destroy on refresh mutex failed"); |
1068 | 0 | } |
1069 | | // this is a singleton, make sure we call destroy only once |
1070 | 0 | _oidc_refresh_mutex = NULL; |
1071 | 0 | } |
1072 | 0 | if (cfg->metrics_hook_data != NULL) { |
1073 | 0 | if (oidc_metrics_cleanup(s) != APR_SUCCESS) { |
1074 | 0 | oidc_serror(s, "oidc_metrics_cleanup failed"); |
1075 | 0 | } |
1076 | | cfg->metrics_hook_data = NULL; |
1077 | 0 | } |
1078 | 0 | } |