Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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&param1=value1";
141
2
  request->args = "foo=bar&param1=value1";
142
2
  apr_uri_parse(request->pool, "https://www.example.com/bla?foo=bar&param1=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
7.04k
apr_pool_t *oidc_test_pool_get(void) {
224
7.04k
  return pool;
225
7.04k
}
226
227
7.04k
request_rec *oidc_test_request_get(void) {
228
7.04k
  return request;
229
7.04k
}
230
231
0
oidc_cfg_t *oidc_test_cfg_get(void) {
232
0
  return (oidc_cfg_t *)ap_get_module_config(request->server->module_config, &auth_openidc_module);
233
0
}
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
}