Coverage Report

Created: 2026-09-28 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/json.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
 * This is the single translation unit that talks to the JSON backend library (libjansson).
45
 * The opaque oidc_json_t type, the oidc_json_* functions and the OIDC_JSON_* constants declared
46
 * in json.h shield the rest of the module from the backend; oidc_json_t is identical to the
47
 * backend value type (struct json_t), so the thin wrappers below pass pointers through without
48
 * casting. Backend-specific concepts (encode/decode flags, value types) are translated here.
49
 */
50
51
#include <limits.h>
52
53
#include <jansson.h>
54
55
#include "const.h"
56
57
#include "json.h"
58
#include "proto/proto.h"
59
#include "util/util.h"
60
61
/*
62
 * translate the backend-independent OIDC_JSON_* flags into libjansson encode/decode flags
63
 */
64
896k
static size_t oidc_json_flags2backend(int flags) {
65
896k
  size_t f = 0;
66
896k
  int indent = 0;
67
896k
  if (flags & OIDC_JSON_COMPACT)
68
860k
    f |= JSON_COMPACT;
69
896k
  if (flags & OIDC_JSON_PRESERVE_ORDER)
70
854k
    f |= JSON_PRESERVE_ORDER;
71
896k
  if (flags & OIDC_JSON_ENCODE_ANY)
72
12.5k
    f |= JSON_ENCODE_ANY;
73
896k
  if (flags & OIDC_JSON_DECODE_ANY)
74
0
    f |= JSON_DECODE_ANY;
75
896k
  indent = (flags >> 8) & 0x1F;
76
896k
  if (indent > 0)
77
0
    f |= JSON_INDENT(indent);
78
896k
  return f;
79
896k
}
80
81
/*
82
 * construction
83
 */
84
7.80k
oidc_json_t *oidc_json_object(void) {
85
7.80k
  return json_object();
86
7.80k
}
87
88
0
oidc_json_t *oidc_json_array(void) {
89
0
  return json_array();
90
0
}
91
92
51.1k
oidc_json_t *oidc_json_string(const char *value) {
93
51.1k
  return json_string(value);
94
51.1k
}
95
96
14.1k
oidc_json_t *oidc_json_integer(oidc_json_int_t value) {
97
14.1k
  return json_integer((json_int_t)value);
98
14.1k
}
99
100
0
oidc_json_t *oidc_json_boolean(int value) {
101
0
  return json_boolean(value);
102
0
}
103
104
/*
105
 * reference counting and copying
106
 */
107
41.6k
void oidc_json_decref(oidc_json_t *json) {
108
41.6k
  json_decref(json);
109
41.6k
}
110
111
2.02k
oidc_json_t *oidc_json_copy(const oidc_json_t *json) {
112
2.02k
  return json_copy((oidc_json_t *)json);
113
2.02k
}
114
115
4
oidc_json_t *oidc_json_deep_copy(const oidc_json_t *json) {
116
4
  return json_deep_copy(json);
117
4
}
118
119
/*
120
 * type inspection
121
 */
122
818k
oidc_json_type_t oidc_json_typeof(const oidc_json_t *json) {
123
818k
  switch (json_typeof(json)) {
124
1.32k
  case JSON_OBJECT:
125
1.32k
    return OIDC_JSON_TYPE_OBJECT;
126
878
  case JSON_ARRAY:
127
878
    return OIDC_JSON_TYPE_ARRAY;
128
0
  case JSON_STRING:
129
0
    return OIDC_JSON_TYPE_STRING;
130
715k
  case JSON_INTEGER:
131
715k
    return OIDC_JSON_TYPE_INTEGER;
132
44.7k
  case JSON_REAL:
133
44.7k
    return OIDC_JSON_TYPE_REAL;
134
678
  case JSON_TRUE:
135
678
    return OIDC_JSON_TYPE_TRUE;
136
1.83k
  case JSON_FALSE:
137
1.83k
    return OIDC_JSON_TYPE_FALSE;
138
53.0k
  case JSON_NULL:
139
53.0k
  default:
140
53.0k
    return OIDC_JSON_TYPE_NULL;
141
818k
  }
142
818k
}
143
144
37.7k
int oidc_json_is_object(const oidc_json_t *json) {
145
37.7k
  return json_is_object(json);
146
37.7k
}
147
148
7.55k
int oidc_json_is_array(const oidc_json_t *json) {
149
7.55k
  return json_is_array(json);
150
7.55k
}
151
152
3.95M
int oidc_json_is_string(const oidc_json_t *json) {
153
3.95M
  return json_is_string(json);
154
3.95M
}
155
156
16.6k
int oidc_json_is_integer(const oidc_json_t *json) {
157
16.6k
  return json_is_integer(json);
158
16.6k
}
159
160
5.95k
int oidc_json_is_real(const oidc_json_t *json) {
161
5.95k
  return json_is_real(json);
162
5.95k
}
163
164
978
int oidc_json_is_number(const oidc_json_t *json) {
165
978
  return json_is_number(json);
166
978
}
167
168
172k
int oidc_json_is_boolean(const oidc_json_t *json) {
169
172k
  return json_is_boolean(json);
170
172k
}
171
172
2.87k
int oidc_json_is_true(const oidc_json_t *json) {
173
2.87k
  return json_is_true(json);
174
2.87k
}
175
176
0
int oidc_json_is_null(const oidc_json_t *json) {
177
0
  return json_is_null(json);
178
0
}
179
180
/*
181
 * scalar value access
182
 */
183
2.39M
const char *oidc_json_string_value(const oidc_json_t *json) {
184
2.39M
  return json_string_value(json);
185
2.39M
}
186
187
14.5k
oidc_json_int_t oidc_json_integer_value(const oidc_json_t *json) {
188
14.5k
  return (oidc_json_int_t)json_integer_value(json);
189
14.5k
}
190
191
1.11k
double oidc_json_real_value(const oidc_json_t *json) {
192
1.11k
  return json_real_value(json);
193
1.11k
}
194
195
978
double oidc_json_number_value(const oidc_json_t *json) {
196
978
  return json_number_value(json);
197
978
}
198
199
0
void oidc_json_integer_set(oidc_json_t *json, oidc_json_int_t value) {
200
0
  json_integer_set(json, (json_int_t)value);
201
0
}
202
203
/*
204
 * object access/mutation
205
 */
206
217k
oidc_json_t *oidc_json_object_get(const oidc_json_t *json, const char *key) {
207
217k
  return json_object_get(json, key);
208
217k
}
209
210
6.38k
int oidc_json_object_set(oidc_json_t *json, const char *key, oidc_json_t *value) {
211
6.38k
  return json_object_set(json, key, value);
212
6.38k
}
213
214
65.6k
int oidc_json_object_set_new(oidc_json_t *json, const char *key, oidc_json_t *value) {
215
65.6k
  return json_object_set_new(json, key, value);
216
65.6k
}
217
218
1.17k
int oidc_json_object_del(oidc_json_t *json, const char *key) {
219
1.17k
  return json_object_del(json, key);
220
1.17k
}
221
222
/*
223
 * object iteration
224
 */
225
1.40k
void *oidc_json_object_iter(oidc_json_t *json) {
226
1.40k
  return json_object_iter(json);
227
1.40k
}
228
229
21.3k
void *oidc_json_object_iter_next(oidc_json_t *json, void *iter) {
230
21.3k
  return json_object_iter_next(json, iter);
231
21.3k
}
232
233
21.3k
const char *oidc_json_object_iter_key(void *iter) {
234
21.3k
  return json_object_iter_key(iter);
235
21.3k
}
236
237
21.3k
oidc_json_t *oidc_json_object_iter_value(void *iter) {
238
21.3k
  return json_object_iter_value(iter);
239
21.3k
}
240
241
/*
242
 * array access/mutation
243
 */
244
3.87M
size_t oidc_json_array_size(const oidc_json_t *json) {
245
3.87M
  return json_array_size(json);
246
3.87M
}
247
248
4.74M
oidc_json_t *oidc_json_array_get(const oidc_json_t *json, size_t index) {
249
4.74M
  return json_array_get(json, index);
250
4.74M
}
251
252
0
int oidc_json_array_append_new(oidc_json_t *json, oidc_json_t *value) {
253
0
  return json_array_append_new(json, value);
254
0
}
255
256
/*
257
 * encode a JSON object into a pool-allocated string
258
 */
259
860k
char *oidc_json_encode(apr_pool_t *pool, const oidc_json_t *json, int flags) {
260
860k
  if (json == NULL)
261
0
    return NULL;
262
860k
  char *s = json_dumps(json, oidc_json_flags2backend(flags));
263
860k
  char *s_value = apr_pstrdup(pool, s);
264
860k
  free(s);
265
860k
  return s_value;
266
860k
}
267
268
3.81k
#define OIDC_JSON_MAX_ERROR_STR 4096
269
270
/*
271
 * parse a string into a JSON value; pool-based and silent, optionally returning an error message
272
 */
273
36.9k
apr_byte_t oidc_json_parse(apr_pool_t *pool, const char *str, int flags, oidc_json_t **json, char **s_err) {
274
36.9k
  json_error_t json_error;
275
276
36.9k
  *json = NULL;
277
36.9k
  if (s_err != NULL)
278
36.9k
    *s_err = NULL;
279
280
36.9k
  if (str == NULL) {
281
0
    if (s_err != NULL)
282
0
      *s_err = apr_pstrdup(pool, "input string is NULL");
283
0
    return FALSE;
284
0
  }
285
286
36.9k
  *json = json_loads(str, oidc_json_flags2backend(flags), &json_error);
287
36.9k
  if (*json == NULL) {
288
4.26k
    if (s_err != NULL) {
289
4.26k
#if JANSSON_VERSION_HEX >= 0x020B00
290
4.26k
      if (json_error_code(&json_error) == json_error_null_character)
291
442
        *s_err = apr_pstrdup(pool, json_error.text);
292
3.81k
      else
293
3.81k
#endif
294
3.81k
        *s_err = apr_psprintf(pool, "%s (%s)", json_error.text,
295
3.81k
                  apr_pstrndup(pool, str, OIDC_JSON_MAX_ERROR_STR));
296
4.26k
    }
297
4.26k
    return FALSE;
298
4.26k
  }
299
300
32.7k
  return TRUE;
301
36.9k
}
302
303
/*
304
 * parse a JSON object
305
 */
306
57.3k
apr_byte_t oidc_json_decode_object_err(request_rec *r, const char *str, oidc_json_t **json, apr_byte_t log_err) {
307
57.3k
  char *s_err = NULL;
308
309
57.3k
  if (str == NULL)
310
20.3k
    return FALSE;
311
312
  /* decode the JSON contents of the buffer */
313
36.9k
  if (oidc_json_parse(r->pool, str, 0, json, &s_err) == FALSE) {
314
    /* something went wrong */
315
4.26k
    if (log_err)
316
4.26k
      oidc_error(r, "JSON parsing returned an error: %s", s_err);
317
4.26k
    return FALSE;
318
4.26k
  }
319
320
32.7k
  if (!oidc_json_is_object(*json)) {
321
    /* a successfully parsed non-object (e.g. a top-level array) is still not a valid result here;
322
     * log_err controls only whether we log the rejection, not whether we reject */
323
400
    if (log_err)
324
400
      oidc_error(r, "parsed JSON did not contain a JSON object");
325
400
    oidc_json_decref(*json);
326
400
    *json = NULL;
327
400
    return FALSE;
328
400
  }
329
330
32.3k
  return TRUE;
331
32.7k
}
332
333
57.3k
apr_byte_t oidc_json_decode_object(request_rec *r, const char *str, oidc_json_t **json) {
334
57.3k
  return oidc_json_decode_object_err(r, str, json, TRUE);
335
57.3k
}
336
337
/*
338
 * printout a JSON string value
339
 */
340
0
static apr_byte_t oidc_json_string_print(request_rec *r, const oidc_json_t *result, const char *key, const char *log) {
341
0
  const oidc_json_t *value = oidc_json_object_get(result, key);
342
0
  if (value != NULL && !oidc_json_is_null(value)) {
343
0
    oidc_error(r, "%s: response contained an \"%s\" entry with value: \"%s\"", log, key,
344
0
         oidc_json_encode(r->pool, value,
345
0
              OIDC_JSON_PRESERVE_ORDER | OIDC_JSON_COMPACT | OIDC_JSON_ENCODE_ANY));
346
0
    return TRUE;
347
0
  }
348
0
  return FALSE;
349
0
}
350
351
/*
352
 * check a JSON object for "error" results and printout
353
 */
354
0
apr_byte_t oidc_json_check_error(request_rec *r, const oidc_json_t *json) {
355
0
  if (oidc_json_string_print(r, json, OIDC_PROTO_ERROR, "oidc_util_check_json_error") == TRUE) {
356
0
    oidc_json_string_print(r, json, OIDC_PROTO_ERROR_DESCRIPTION, "oidc_util_check_json_error");
357
0
    return TRUE;
358
0
  }
359
0
  return FALSE;
360
0
}
361
362
/*
363
 * decode a JSON string, check for "error" results and printout
364
 */
365
0
apr_byte_t oidc_json_decode_and_check_error(request_rec *r, const char *str, oidc_json_t **json) {
366
367
0
  if (oidc_json_decode_object(r, str, json) == FALSE)
368
0
    return FALSE;
369
370
  // see if it is an error response
371
0
  if (oidc_json_check_error(r, *json) == TRUE) {
372
0
    oidc_json_decref(*json);
373
0
    *json = NULL;
374
0
    return FALSE;
375
0
  }
376
377
0
  return TRUE;
378
0
}
379
380
/*
381
 * see if a certain string value is part of a JSON array with string elements
382
 */
383
1.01k
apr_byte_t oidc_json_array_has_value(request_rec *r, const oidc_json_t *haystack, const char *needle) {
384
385
1.01k
  if ((haystack == NULL) || (!oidc_json_is_array(haystack)))
386
0
    return FALSE;
387
388
1.01k
  size_t i;
389
1.92M
  for (i = 0; i < oidc_json_array_size(haystack); i++) {
390
1.91M
    const oidc_json_t *elem = oidc_json_array_get(haystack, i);
391
1.91M
    if (!oidc_json_is_string(elem)) {
392
818k
      oidc_error(r, "unhandled in-array JSON non-string object type [%d]", oidc_json_typeof(elem));
393
818k
      continue;
394
818k
    }
395
1.10M
    if (_oidc_strcmp(oidc_json_string_value(elem), needle) == 0) {
396
534
      break;
397
534
    }
398
1.10M
  }
399
400
1.01k
  return (i == oidc_json_array_size(haystack)) ? FALSE : TRUE;
401
1.01k
}
402
403
/*
404
 * get (optional) string from a JSON object
405
 */
406
apr_byte_t oidc_json_object_get_string(apr_pool_t *pool, const oidc_json_t *json, const char *name, char **value,
407
83.6k
               const char *default_value) {
408
83.6k
  *value = default_value ? apr_pstrdup(pool, default_value) : NULL;
409
83.6k
  if (json != NULL) {
410
83.4k
    const oidc_json_t *v = oidc_json_object_get(json, name);
411
83.4k
    if ((v != NULL) && (oidc_json_is_string(v))) {
412
3.42k
      *value = apr_pstrdup(pool, oidc_json_string_value(v));
413
3.42k
    }
414
83.4k
  }
415
83.6k
  return TRUE;
416
83.6k
}
417
418
/*
419
 * get (optional) string array from a JSON object
420
 */
421
41
static void oidc_json_string_array_append(apr_pool_t *pool, const oidc_json_t *arr, apr_array_header_t *value) {
422
803k
  for (size_t i = 0; i < oidc_json_array_size(arr); i++) {
423
803k
    const oidc_json_t *v = oidc_json_array_get(arr, i);
424
    /* skip non-string elements rather than pushing a NULL (oidc_json_string_value returns NULL
425
     * for them), matching the single-string getter above */
426
803k
    if (oidc_json_is_string(v))
427
169k
      APR_ARRAY_PUSH(value, const char *) = apr_pstrdup(pool, oidc_json_string_value(v));
428
803k
  }
429
41
}
430
431
apr_byte_t oidc_json_object_get_string_array(apr_pool_t *pool, const oidc_json_t *json, const char *name,
432
1.72k
               apr_array_header_t **value, const apr_array_header_t *default_value) {
433
1.72k
  const oidc_json_t *arr = NULL;
434
1.72k
  *value = (default_value != NULL) ? apr_array_copy(pool, default_value) : NULL;
435
1.72k
  if (json != NULL) {
436
1.72k
    arr = oidc_json_object_get(json, name);
437
1.72k
    if ((arr != NULL) && (oidc_json_is_array(arr))) {
438
41
      *value = apr_array_make(pool, (int)oidc_json_array_size(arr), sizeof(const char *));
439
41
      oidc_json_string_array_append(pool, arr, *value);
440
41
    }
441
1.72k
  }
442
1.72k
  return TRUE;
443
1.72k
}
444
445
/*
446
 * get (optional) int from a JSON object
447
 */
448
13.7k
apr_byte_t oidc_json_object_get_int(const oidc_json_t *json, const char *name, int *value, const int default_value) {
449
13.7k
  const oidc_json_t *v = NULL;
450
13.7k
  *value = default_value;
451
13.7k
  if (json != NULL) {
452
13.7k
    v = oidc_json_object_get(json, name);
453
13.7k
    if ((v != NULL) && (oidc_json_is_integer(v))) {
454
      /* oidc_json_int_t is at least int64; clamp into int range to avoid silent truncation */
455
514
      oidc_json_int_t n = oidc_json_integer_value(v);
456
514
      if (n > INT_MAX)
457
13
        *value = INT_MAX;
458
501
      else if (n < INT_MIN)
459
67
        *value = INT_MIN;
460
434
      else
461
434
        *value = (int)n;
462
514
      return TRUE;
463
514
    }
464
13.7k
  }
465
13.2k
  return FALSE;
466
13.7k
}
467
468
/*
469
 * get (optional) 64-bit integer from a JSON object, preserving the full value - unlike
470
 * oidc_json_object_get_int, which clamps into int range; used for timestamps that must survive 2038
471
 */
472
apr_byte_t oidc_json_object_get_int64(const oidc_json_t *json, const char *name, oidc_json_int_t *value,
473
0
              const oidc_json_int_t default_value) {
474
0
  const oidc_json_t *v = NULL;
475
0
  *value = default_value;
476
0
  if (json != NULL) {
477
0
    v = oidc_json_object_get(json, name);
478
0
    if ((v != NULL) && (oidc_json_is_integer(v))) {
479
0
      *value = oidc_json_integer_value(v);
480
0
      return TRUE;
481
0
    }
482
0
  }
483
0
  return FALSE;
484
0
}
485
486
/*
487
 * get (optional) boolean from a JSON object
488
 */
489
6.84k
apr_byte_t oidc_json_object_get_bool(const oidc_json_t *json, const char *name, int *value, const int default_value) {
490
6.84k
  const oidc_json_t *v = NULL;
491
6.84k
  *value = default_value;
492
6.84k
  if (json != NULL) {
493
6.84k
    v = oidc_json_object_get(json, name);
494
6.84k
    if ((v != NULL) && (oidc_json_is_boolean(v))) {
495
7
      *value = oidc_json_is_true(v);
496
7
      return TRUE;
497
7
    }
498
6.84k
  }
499
6.83k
  return FALSE;
500
6.84k
}
501
502
/*
503
 * merge two JSON objects
504
 */
505
0
apr_byte_t oidc_json_merge(request_rec *r, oidc_json_t *src, oidc_json_t *dst) {
506
507
0
  const char *key;
508
0
  oidc_json_t *value = NULL;
509
0
  void *iter = NULL;
510
511
0
  if ((src == NULL) || (dst == NULL))
512
0
    return FALSE;
513
514
0
  oidc_debug(r, "src=%s, dst=%s", oidc_json_encode(r->pool, src, OIDC_JSON_PRESERVE_ORDER | OIDC_JSON_COMPACT),
515
0
       oidc_json_encode(r->pool, dst, OIDC_JSON_PRESERVE_ORDER | OIDC_JSON_COMPACT));
516
517
0
  iter = oidc_json_object_iter(src);
518
0
  while (iter) {
519
0
    key = oidc_json_object_iter_key(iter);
520
0
    value = oidc_json_object_iter_value(iter);
521
0
    oidc_json_object_set(dst, key, value);
522
0
    iter = oidc_json_object_iter_next(src, iter);
523
0
  }
524
525
0
  oidc_debug(r, "result dst=%s", oidc_json_encode(r->pool, dst, OIDC_JSON_PRESERVE_ORDER | OIDC_JSON_COMPACT));
526
527
0
  return TRUE;
528
0
}