/src/mod_auth_openidc/test/util.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 | | |
44 | | #include "util.h" |
45 | | #include "cfg/cfg_int.h" |
46 | | #include "cfg/dir.h" |
47 | | #include "handle/handle.h" |
48 | | #include "metadata.h" |
49 | | #include "proto/proto.h" |
50 | | #include "session.h" |
51 | | #include "util/util.h" |
52 | | #include <openssl/evp.h> |
53 | | |
54 | | /* Per-test fixture state; module-level test statics need their own CK_FORK=no reset. */ |
55 | | static apr_pool_t *pool = NULL; |
56 | | static request_rec *request = NULL; |
57 | | |
58 | | /* Cache PBKDF2 by secret across tests; direct KDF tests bypass this optimization. */ |
59 | 2 | #define OIDC_TEST_KDF_CACHE_MAX 32 |
60 | | static struct { |
61 | | char secret[128]; |
62 | | unsigned char key[OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN]; |
63 | | } oidc_test_kdf_cache[OIDC_TEST_KDF_CACHE_MAX]; |
64 | | static int oidc_test_kdf_cache_n = 0; |
65 | | |
66 | 2 | static apr_byte_t oidc_test_key_derive_cached(const char *secret, unsigned char *out) { |
67 | 2 | int i; |
68 | 2 | for (i = 0; i < oidc_test_kdf_cache_n; i++) { |
69 | 0 | if (_oidc_strcmp(oidc_test_kdf_cache[i].secret, secret) == 0) { |
70 | 0 | _oidc_memcpy(out, oidc_test_kdf_cache[i].key, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN); |
71 | 0 | return TRUE; |
72 | 0 | } |
73 | 0 | } |
74 | 2 | if (oidc_util_key_derive_passphrase_key(secret, out, OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN) == FALSE) |
75 | 0 | return FALSE; |
76 | 2 | if ((oidc_test_kdf_cache_n < OIDC_TEST_KDF_CACHE_MAX) && |
77 | 2 | (_oidc_strlen(secret) < sizeof(oidc_test_kdf_cache[0].secret))) { |
78 | 2 | _oidc_strcpy(oidc_test_kdf_cache[oidc_test_kdf_cache_n].secret, secret); |
79 | 2 | _oidc_memcpy(oidc_test_kdf_cache[oidc_test_kdf_cache_n].key, out, |
80 | 2 | OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN); |
81 | 2 | oidc_test_kdf_cache_n++; |
82 | 2 | } |
83 | 2 | return TRUE; |
84 | 2 | } |
85 | | |
86 | | /* |
87 | | * test-only drop-in for oidc_cfg_crypto_passphrase_derive_keys()/oidc_crypto_passphrase_derive_keys() |
88 | | * that routes the actual KDF work through the cache above; same semantics (skips a slot whose |
89 | | * *_set flag is already TRUE, treats an empty secret as "not configured"). |
90 | | */ |
91 | 2 | static apr_byte_t oidc_test_crypto_passphrase_derive_keys_cached(oidc_crypto_passphrase_t *cp) { |
92 | 2 | if ((cp->secret1 != NULL) && (_oidc_strlen(cp->secret1) > 0) && (cp->derived_key1_set == FALSE)) { |
93 | 2 | if (oidc_test_key_derive_cached(cp->secret1, cp->derived_key1) == FALSE) |
94 | 0 | return FALSE; |
95 | 2 | cp->derived_key1_set = TRUE; |
96 | 2 | } |
97 | 2 | if ((cp->secret2 != NULL) && (_oidc_strlen(cp->secret2) > 0) && (cp->derived_key2_set == FALSE)) { |
98 | 0 | if (oidc_test_key_derive_cached(cp->secret2, cp->derived_key2) == FALSE) |
99 | 0 | return FALSE; |
100 | 0 | cp->derived_key2_set = TRUE; |
101 | 0 | } |
102 | 2 | return TRUE; |
103 | 2 | } |
104 | | |
105 | 2 | static request_rec *oidc_test_request_init(apr_pool_t *pool) { |
106 | 2 | const unsigned int kIdx = 0; |
107 | 2 | const unsigned int kEls = kIdx + 1; |
108 | 2 | request_rec *request = (request_rec *)apr_pcalloc(pool, sizeof(request_rec)); |
109 | | |
110 | 2 | apr_pool_create(&request->pool, pool); |
111 | | |
112 | 2 | request->subprocess_env = apr_table_make(request->pool, 0); |
113 | | |
114 | 2 | request->headers_in = apr_table_make(request->pool, 0); |
115 | 2 | request->headers_out = apr_table_make(request->pool, 0); |
116 | 2 | request->err_headers_out = apr_table_make(request->pool, 0); |
117 | | |
118 | 2 | apr_table_set(request->headers_in, "Host", "www.example.com"); |
119 | 2 | apr_table_set(request->headers_in, "OIDC_foo", "some-value"); |
120 | 2 | apr_table_set(request->headers_in, "Cookie", |
121 | 2 | "foo=bar; " |
122 | 2 | "mod_auth_openidc_session" |
123 | 2 | "=0123456789abcdef; baz=zot"); |
124 | | |
125 | 2 | request->server = apr_pcalloc(pool, sizeof(struct server_rec)); |
126 | 2 | request->server->process = apr_pcalloc(pool, sizeof(struct process_rec)); |
127 | 2 | apr_pool_create(&request->server->process->pool, pool); |
128 | 2 | apr_pool_create(&request->server->process->pconf, pool); |
129 | 2 | request->connection = apr_pcalloc(pool, sizeof(struct conn_rec)); |
130 | 2 | request->connection->bucket_alloc = apr_bucket_alloc_create(pool); |
131 | 2 | request->connection->local_addr = apr_pcalloc(pool, sizeof(apr_sockaddr_t)); |
132 | | /* minimal output filter carrying the request so the ap_pass_brigade stub |
133 | | * can capture sent response bodies into the "sent_body" request state */ |
134 | 2 | request->output_filters = apr_pcalloc(pool, sizeof(ap_filter_t)); |
135 | 2 | request->output_filters->r = request; |
136 | | |
137 | 2 | apr_pool_userdata_set("https", "scheme", NULL, request->pool); |
138 | 2 | request->server->server_hostname = "www.example.com"; |
139 | 2 | request->connection->local_addr->port = 4433; |
140 | 2 | request->unparsed_uri = "/bla?foo=bar¶m1=value1"; |
141 | 2 | request->args = "foo=bar¶m1=value1"; |
142 | 2 | apr_uri_parse(request->pool, "https://www.example.com/bla?foo=bar¶m1=value1", &request->parsed_uri); |
143 | | |
144 | 2 | auth_openidc_module.module_index = kIdx; |
145 | 2 | oidc_cfg_t *cfg = oidc_cfg_server_create(request->server->process->pconf, request->server); |
146 | | |
147 | 2 | oidc_cfg_provider_issuer_set(request->server->process->pconf, oidc_cfg_provider_get(cfg), |
148 | 2 | "https://idp.example.com"); |
149 | 2 | oidc_cfg_provider_authorization_endpoint_url_set(request->server->process->pconf, oidc_cfg_provider_get(cfg), |
150 | 2 | "https://idp.example.com/authorize"); |
151 | 2 | oidc_cfg_provider_client_id_set(request->server->process->pconf, oidc_cfg_provider_get(cfg), "client_id"); |
152 | | |
153 | 2 | cfg->redirect_uri = "https://www.example.com/protected/"; |
154 | | |
155 | 2 | oidc_dir_cfg_t *d_cfg = oidc_cfg_dir_config_create(request->server->process->pconf, NULL); |
156 | | |
157 | | // coverity[suspicious_sizeof] |
158 | 2 | request->server->module_config = apr_pcalloc(request->server->process->pconf, sizeof(void *) * kEls); |
159 | | // coverity[suspicious_sizeof] |
160 | 2 | request->per_dir_config = apr_pcalloc(request->server->process->pconf, sizeof(void *) * kEls); |
161 | 2 | ap_set_module_config(request->server->module_config, &auth_openidc_module, cfg); |
162 | 2 | ap_set_module_config(request->per_dir_config, &auth_openidc_module, d_cfg); |
163 | | |
164 | | // TODO: |
165 | 2 | cfg->public_keys = apr_array_make(request->server->process->pconf, 1, sizeof(const char *)); |
166 | 2 | cfg->private_keys = apr_array_make(request->server->process->pconf, 1, sizeof(const char *)); |
167 | | |
168 | 2 | cfg->crypto_passphrase.secret1 = "12345678901234567890123456789012"; |
169 | 2 | if (oidc_test_crypto_passphrase_derive_keys_cached(&cfg->crypto_passphrase) == FALSE) { |
170 | 0 | fprintf(stderr, "oidc_cfg_crypto_passphrase_derive_keys failed!\n"); |
171 | 0 | exit(-1); |
172 | 0 | } |
173 | 2 | cfg->cache.impl = &oidc_cache_shm; |
174 | 2 | cfg->cache.cfg = NULL; |
175 | 2 | cfg->cache.shm_size_max = 500; |
176 | 2 | const char *shm_size = getenv("OIDC_TEST_SHM_SIZE"); |
177 | 2 | if (shm_size != NULL) { |
178 | 0 | char *end = NULL; |
179 | 0 | long parsed = strtol(shm_size, &end, 10); |
180 | 0 | if ((end != shm_size) && (*end == '\0') && (parsed >= 128) && (parsed <= 1000000)) |
181 | 0 | cfg->cache.shm_size_max = (int)parsed; |
182 | 0 | } |
183 | 2 | cfg->cache.shm_entry_size_max = 16384 + 255 + 17; |
184 | 2 | cfg->cache.encrypt = 1; |
185 | | /* full post-config so the cache backend AND the shared refresh-grant mutex get set up */ |
186 | 2 | if (oidc_cfg_post_config(request->server->process->pconf, cfg, request->server) != OK) { |
187 | 0 | fprintf(stderr, "oidc_cfg_post_config failed!\n"); |
188 | 0 | exit(-1); |
189 | 0 | } |
190 | | |
191 | 2 | if (oidc_cfg_dir_post_config(request->server) != OK) { |
192 | 0 | fprintf(stderr, "oidc_cfg_dir_post_config failed!\n"); |
193 | 0 | exit(-1); |
194 | 0 | } |
195 | | |
196 | 2 | oidc_http_curl_pool_init(request->server->process->pconf); |
197 | | |
198 | 2 | return request; |
199 | 2 | } |
200 | | |
201 | 2 | void oidc_test_setup(void) { |
202 | 2 | apr_initialize(); |
203 | 2 | oidc_pre_config_init(); |
204 | | /* reset the stubbed AuthType so a test that changed it does not leak into |
205 | | * the next one under CK_FORK=no */ |
206 | 2 | oidc_test_set_auth_type(NULL); |
207 | 2 | apr_pool_create(&pool, NULL); |
208 | 2 | request = oidc_test_request_init(pool); |
209 | 2 | } |
210 | | |
211 | 0 | void oidc_test_teardown(void) { |
212 | | /* release the process-wide refresh mutex before apr_terminate frees its pool */ |
213 | 0 | if (request != NULL) { |
214 | 0 | oidc_cfg_t *cfg = oidc_test_cfg_get(); |
215 | 0 | oidc_cfg_process_cleanup(cfg, request->server); |
216 | 0 | } |
217 | 0 | EVP_cleanup(); |
218 | 0 | apr_terminate(); |
219 | 0 | request = NULL; |
220 | 0 | pool = NULL; |
221 | 0 | } |
222 | | |
223 | 2.51k | apr_pool_t *oidc_test_pool_get(void) { |
224 | 2.51k | return pool; |
225 | 2.51k | } |
226 | | |
227 | 2.51k | request_rec *oidc_test_request_get(void) { |
228 | 2.51k | return request; |
229 | 2.51k | } |
230 | | |
231 | 2.51k | oidc_cfg_t *oidc_test_cfg_get(void) { |
232 | 2.51k | return (oidc_cfg_t *)ap_get_module_config(request->server->module_config, &auth_openidc_module); |
233 | 2.51k | } |
234 | | |
235 | 0 | cmd_parms *oidc_test_cmd_get(const char *primitive) { |
236 | 0 | request_rec *r = oidc_test_request_get(); |
237 | 0 | cmd_parms *cmd = apr_pcalloc(r->pool, sizeof(cmd_parms)); |
238 | 0 | cmd->server = r->server; |
239 | 0 | cmd->pool = r->pool; |
240 | 0 | cmd->temp_pool = r->pool; |
241 | 0 | cmd->directive = apr_pcalloc(cmd->pool, sizeof(ap_directive_t)); |
242 | 0 | cmd->directive->directive = primitive; |
243 | 0 | return cmd; |
244 | 0 | } |
245 | | |
246 | | /* Re-derive keys after tests directly replace passphrase secrets at runtime. */ |
247 | 0 | void oidc_test_crypto_passphrase_rederive(oidc_cfg_t *cfg) { |
248 | 0 | cfg->crypto_passphrase.derived_key1_set = FALSE; |
249 | 0 | cfg->crypto_passphrase.derived_key2_set = FALSE; |
250 | 0 | if (oidc_test_crypto_passphrase_derive_keys_cached(&cfg->crypto_passphrase) == FALSE) { |
251 | | fprintf(stderr, "oidc_cfg_crypto_passphrase_derive_keys failed!\n"); |
252 | 0 | exit(-1); |
253 | 0 | } |
254 | 0 | } |