Coverage Report

Created: 2026-09-01 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/metadata.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
 * Copyright (C) 2013-2017 Ping Identity Corporation
23
 * All rights reserved.
24
 *
25
 * Thin orchestrator over the metadata subsystem. The per-domain helpers live
26
 * under src/metadata/ (provider.c, conf.c, client.c, oauth.c, jwks.c, util.c).
27
 *
28
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
29
 */
30
31
#include "metadata/internal.h"
32
33
#include <apr_file_io.h>
34
35
/*
36
 * get the metadata for a specified issuer
37
 *
38
 * fills the oidc_provider_t struct by reading and merging the provider, conf
39
 * and client metadata files for the issuer
40
 */
41
apr_byte_t oidc_metadata_get(request_rec *r, oidc_cfg_t *cfg, const char *issuer, oidc_provider_t **provider,
42
0
           apr_byte_t allow_discovery) {
43
44
0
  apr_byte_t rc = FALSE;
45
46
  /* pointers to the parsed JSON metadata */
47
0
  oidc_json_t *j_provider = NULL;
48
0
  oidc_json_t *j_client = NULL;
49
0
  oidc_json_t *j_conf = NULL;
50
51
  /* allocate space for a parsed-and-merged metadata struct */
52
0
  *provider = oidc_cfg_provider_create(r->pool);
53
54
  /*
55
   * read and parse the provider, conf and client metadata respectively
56
   * NB: order is important here
57
   */
58
59
0
  if (oidc_metadata_provider_get(r, cfg, issuer, &j_provider, allow_discovery) == FALSE)
60
0
    goto end;
61
0
  if (oidc_metadata_conf_get(r, issuer, &j_conf) == FALSE)
62
0
    goto end;
63
64
  /* the provider metadata is parsed against the settings of this OP, so those of its conf
65
   * metadata that it depends on are applied to the (still empty) provider struct up front */
66
0
  oidc_metadata_conf_parse_pre_provider(r, cfg, j_conf, *provider);
67
68
0
  if (oidc_metadata_provider_parse(r, cfg, j_provider, *provider) == FALSE)
69
0
    goto end;
70
0
  if (oidc_metadata_conf_parse(r, cfg, j_conf, *provider) == FALSE)
71
0
    goto end;
72
73
0
  if (oidc_metadata_client_get(r, cfg, issuer, *provider, &j_client) == FALSE)
74
0
    goto end;
75
0
  if (oidc_metadata_client_parse(r, cfg, j_client, *provider) == FALSE)
76
0
    goto end;
77
78
0
  rc = TRUE;
79
80
0
end:
81
82
0
  if (j_provider)
83
0
    oidc_json_decref(j_provider);
84
0
  if (j_conf)
85
0
    oidc_json_decref(j_conf);
86
0
  if (j_client)
87
0
    oidc_json_decref(j_client);
88
89
0
  return rc;
90
0
}
91
92
/*
93
 * get a list of configured OIDC providers based on the entries in the provider metadata directory
94
 */
95
0
apr_byte_t oidc_metadata_list(request_rec *r, oidc_cfg_t *cfg, apr_array_header_t **list) {
96
0
  apr_status_t rc;
97
0
  apr_dir_t *dir;
98
0
  apr_finfo_t fi;
99
0
  char s_err[128];
100
101
0
  oidc_debug(r, "enter");
102
103
  /* A failed static-provider lookup may reach discovery without a metadata directory. */
104
0
  if (oidc_cfg_metadata_dir_get(cfg) == NULL) {
105
0
    oidc_error(r, "" OIDCMetadataDir " is not set: no providers can be listed");
106
0
    return FALSE;
107
0
  }
108
109
  /* open the metadata directory */
110
0
  if ((rc = apr_dir_open(&dir, oidc_cfg_metadata_dir_get(cfg), r->pool)) != APR_SUCCESS) {
111
0
    oidc_error(r, "error opening metadata directory '%s' (%s)", oidc_cfg_metadata_dir_get(cfg),
112
0
         apr_strerror(rc, s_err, sizeof(s_err)));
113
0
    return FALSE;
114
0
  }
115
116
  /* allocate some space in the array that will hold the list of providers */
117
0
  *list = apr_array_make(r->pool, 5, sizeof(const char *));
118
  /* BTW: we could estimate the number in the array based on # directory entries... */
119
120
  /* loop over the entries in the provider metadata directory */
121
0
  while (apr_dir_read(&fi, APR_FINFO_NAME, dir) == APR_SUCCESS) {
122
123
    /* skip "." and ".." entries */
124
0
    if (fi.name[0] == OIDC_CHAR_DOT)
125
0
      continue;
126
    /* skip other non-provider entries */
127
0
    const char *ext = strrchr(fi.name, OIDC_CHAR_DOT);
128
0
    if (ext == NULL)
129
0
      continue;
130
0
    ext++;
131
0
    if (_oidc_strcmp(ext, OIDC_METADATA_SUFFIX_PROVIDER) != 0)
132
0
      continue;
133
134
    /* get the issuer from the filename */
135
0
    const char *issuer = oidc_metadata_filename_to_issuer(r, fi.name);
136
    /* The legacy filename format strips the scheme, so use the issuer from
137
     * the provider document when it is available. This preserves HTTP
138
     * issuers while retaining the filename-derived fallback for malformed
139
     * entries, which will be rejected by the normal metadata validation. */
140
0
    const char *provider_path = apr_psprintf(r->pool, "%s/%s", oidc_cfg_metadata_dir_get(cfg), fi.name);
141
0
    oidc_json_t *j_provider = NULL;
142
0
    char *metadata_issuer = NULL;
143
0
    if (oidc_metadata_file_read_json(r, provider_path, &j_provider) == TRUE)
144
0
      oidc_json_object_get_string(r->pool, j_provider, OIDC_METADATA_ISSUER, &metadata_issuer, NULL);
145
    /* NB: oidc_json_object_get_string returns TRUE with a NULL value for an absent key,
146
     * so the fallback must check the value, not the return code */
147
0
    if (metadata_issuer != NULL)
148
0
      issuer = metadata_issuer;
149
0
    if (j_provider != NULL)
150
0
      oidc_json_decref(j_provider);
151
152
    /* get the provider and client metadata, do all checks and registration if possible */
153
0
    oidc_provider_t *provider = NULL;
154
0
    if (oidc_metadata_get(r, cfg, issuer, &provider, FALSE) == TRUE) {
155
      /* push the decoded issuer filename in to the array */
156
0
      APR_ARRAY_PUSH(*list, const char *) = oidc_cfg_provider_issuer_get(provider);
157
0
    }
158
0
  }
159
160
  /* we're done, cleanup now */
161
0
  apr_dir_close(dir);
162
163
0
  return TRUE;
164
0
}