Coverage Report

Created: 2026-08-31 07:13

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
2.20k
static size_t oidc_json_flags2backend(int flags) {
65
2.20k
  size_t f = 0;
66
2.20k
  int indent = 0;
67
2.20k
  if (flags & OIDC_JSON_COMPACT)
68
0
    f |= JSON_COMPACT;
69
2.20k
  if (flags & OIDC_JSON_PRESERVE_ORDER)
70
0
    f |= JSON_PRESERVE_ORDER;
71
2.20k
  if (flags & OIDC_JSON_ENCODE_ANY)
72
0
    f |= JSON_ENCODE_ANY;
73
2.20k
  if (flags & OIDC_JSON_DECODE_ANY)
74
0
    f |= JSON_DECODE_ANY;
75
2.20k
  indent = (flags >> 8) & 0x1F;
76
2.20k
  if (indent > 0)
77
0
    f |= JSON_INDENT(indent);
78
2.20k
  return f;
79
2.20k
}
80
81
/*
82
 * construction
83
 */
84
0
oidc_json_t *oidc_json_object(void) {
85
0
  return json_object();
86
0
}
87
88
0
oidc_json_t *oidc_json_array(void) {
89
0
  return json_array();
90
0
}
91
92
0
oidc_json_t *oidc_json_string(const char *value) {
93
0
  return json_string(value);
94
0
}
95
96
0
oidc_json_t *oidc_json_integer(oidc_json_int_t value) {
97
0
  return json_integer((json_int_t)value);
98
0
}
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
22
void oidc_json_decref(oidc_json_t *json) {
108
22
  json_decref(json);
109
22
}
110
111
0
oidc_json_t *oidc_json_copy(const oidc_json_t *json) {
112
0
  return json_copy((oidc_json_t *)json);
113
0
}
114
115
0
oidc_json_t *oidc_json_deep_copy(const oidc_json_t *json) {
116
0
  return json_deep_copy(json);
117
0
}
118
119
/*
120
 * type inspection
121
 */
122
0
oidc_json_type_t oidc_json_typeof(const oidc_json_t *json) {
123
0
  switch (json_typeof(json)) {
124
0
  case JSON_OBJECT:
125
0
    return OIDC_JSON_TYPE_OBJECT;
126
0
  case JSON_ARRAY:
127
0
    return OIDC_JSON_TYPE_ARRAY;
128
0
  case JSON_STRING:
129
0
    return OIDC_JSON_TYPE_STRING;
130
0
  case JSON_INTEGER:
131
0
    return OIDC_JSON_TYPE_INTEGER;
132
0
  case JSON_REAL:
133
0
    return OIDC_JSON_TYPE_REAL;
134
0
  case JSON_TRUE:
135
0
    return OIDC_JSON_TYPE_TRUE;
136
0
  case JSON_FALSE:
137
0
    return OIDC_JSON_TYPE_FALSE;
138
0
  case JSON_NULL:
139
0
  default:
140
0
    return OIDC_JSON_TYPE_NULL;
141
0
  }
142
0
}
143
144
22
int oidc_json_is_object(const oidc_json_t *json) {
145
22
  return json_is_object(json);
146
22
}
147
148
0
int oidc_json_is_array(const oidc_json_t *json) {
149
0
  return json_is_array(json);
150
0
}
151
152
0
int oidc_json_is_string(const oidc_json_t *json) {
153
0
  return json_is_string(json);
154
0
}
155
156
0
int oidc_json_is_integer(const oidc_json_t *json) {
157
0
  return json_is_integer(json);
158
0
}
159
160
0
int oidc_json_is_real(const oidc_json_t *json) {
161
0
  return json_is_real(json);
162
0
}
163
164
0
int oidc_json_is_number(const oidc_json_t *json) {
165
0
  return json_is_number(json);
166
0
}
167
168
0
int oidc_json_is_boolean(const oidc_json_t *json) {
169
0
  return json_is_boolean(json);
170
0
}
171
172
0
int oidc_json_is_true(const oidc_json_t *json) {
173
0
  return json_is_true(json);
174
0
}
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
0
const char *oidc_json_string_value(const oidc_json_t *json) {
184
0
  return json_string_value(json);
185
0
}
186
187
0
oidc_json_int_t oidc_json_integer_value(const oidc_json_t *json) {
188
0
  return (oidc_json_int_t)json_integer_value(json);
189
0
}
190
191
0
double oidc_json_real_value(const oidc_json_t *json) {
192
0
  return json_real_value(json);
193
0
}
194
195
0
double oidc_json_number_value(const oidc_json_t *json) {
196
0
  return json_number_value(json);
197
0
}
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
0
oidc_json_t *oidc_json_object_get(const oidc_json_t *json, const char *key) {
207
0
  return json_object_get(json, key);
208
0
}
209
210
0
int oidc_json_object_set(oidc_json_t *json, const char *key, oidc_json_t *value) {
211
0
  return json_object_set(json, key, value);
212
0
}
213
214
0
int oidc_json_object_set_new(oidc_json_t *json, const char *key, oidc_json_t *value) {
215
0
  return json_object_set_new(json, key, value);
216
0
}
217
218
0
int oidc_json_object_del(oidc_json_t *json, const char *key) {
219
0
  return json_object_del(json, key);
220
0
}
221
222
/*
223
 * object iteration
224
 */
225
0
void *oidc_json_object_iter(oidc_json_t *json) {
226
0
  return json_object_iter(json);
227
0
}
228
229
0
void *oidc_json_object_iter_next(oidc_json_t *json, void *iter) {
230
0
  return json_object_iter_next(json, iter);
231
0
}
232
233
0
const char *oidc_json_object_iter_key(void *iter) {
234
0
  return json_object_iter_key(iter);
235
0
}
236
237
0
oidc_json_t *oidc_json_object_iter_value(void *iter) {
238
0
  return json_object_iter_value(iter);
239
0
}
240
241
/*
242
 * array access/mutation
243
 */
244
0
size_t oidc_json_array_size(const oidc_json_t *json) {
245
0
  return json_array_size(json);
246
0
}
247
248
0
oidc_json_t *oidc_json_array_get(const oidc_json_t *json, size_t index) {
249
0
  return json_array_get(json, index);
250
0
}
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
0
char *oidc_json_encode(apr_pool_t *pool, const oidc_json_t *json, int flags) {
260
0
  if (json == NULL)
261
0
    return NULL;
262
0
  char *s = json_dumps(json, oidc_json_flags2backend(flags));
263
0
  char *s_value = apr_pstrdup(pool, s);
264
0
  free(s);
265
0
  return s_value;
266
0
}
267
268
2.18k
#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
2.20k
apr_byte_t oidc_json_parse(apr_pool_t *pool, const char *str, int flags, oidc_json_t **json, char **s_err) {
274
2.20k
  json_error_t json_error;
275
276
2.20k
  *json = NULL;
277
2.20k
  if (s_err != NULL)
278
2.20k
    *s_err = NULL;
279
280
2.20k
  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
2.20k
  *json = json_loads(str, oidc_json_flags2backend(flags), &json_error);
287
2.20k
  if (*json == NULL) {
288
2.18k
    if (s_err != NULL) {
289
2.18k
#if JANSSON_VERSION_HEX >= 0x020B00
290
2.18k
      if (json_error_code(&json_error) == json_error_null_character)
291
1
        *s_err = apr_pstrdup(pool, json_error.text);
292
2.18k
      else
293
2.18k
#endif
294
2.18k
        *s_err = apr_psprintf(pool, "%s (%s)", json_error.text,
295
2.18k
                  apr_pstrndup(pool, str, OIDC_JSON_MAX_ERROR_STR));
296
2.18k
    }
297
2.18k
    return FALSE;
298
2.18k
  }
299
300
22
  return TRUE;
301
2.20k
}
302
303
/*
304
 * parse a JSON object
305
 */
306
2.20k
apr_byte_t oidc_json_decode_object_err(request_rec *r, const char *str, oidc_json_t **json, apr_byte_t log_err) {
307
2.20k
  char *s_err = NULL;
308
309
2.20k
  if (str == NULL)
310
0
    return FALSE;
311
312
  /* decode the JSON contents of the buffer */
313
2.20k
  if (oidc_json_parse(r->pool, str, 0, json, &s_err) == FALSE) {
314
    /* something went wrong */
315
2.18k
    if (log_err)
316
2.18k
      oidc_error(r, "JSON parsing returned an error: %s", s_err);
317
2.18k
    return FALSE;
318
2.18k
  }
319
320
22
  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
13
    if (log_err)
324
13
      oidc_error(r, "parsed JSON did not contain a JSON object");
325
13
    oidc_json_decref(*json);
326
13
    *json = NULL;
327
13
    return FALSE;
328
13
  }
329
330
9
  return TRUE;
331
22
}
332
333
2.20k
apr_byte_t oidc_json_decode_object(request_rec *r, const char *str, oidc_json_t **json) {
334
2.20k
  return oidc_json_decode_object_err(r, str, json, TRUE);
335
2.20k
}
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
0
apr_byte_t oidc_json_array_has_value(request_rec *r, const oidc_json_t *haystack, const char *needle) {
384
385
0
  if ((haystack == NULL) || (!oidc_json_is_array(haystack)))
386
0
    return FALSE;
387
388
0
  size_t i;
389
0
  for (i = 0; i < oidc_json_array_size(haystack); i++) {
390
0
    const oidc_json_t *elem = oidc_json_array_get(haystack, i);
391
0
    if (!oidc_json_is_string(elem)) {
392
0
      oidc_error(r, "unhandled in-array JSON non-string object type [%d]", oidc_json_typeof(elem));
393
0
      continue;
394
0
    }
395
0
    if (_oidc_strcmp(oidc_json_string_value(elem), needle) == 0) {
396
0
      break;
397
0
    }
398
0
  }
399
400
0
  return (i == oidc_json_array_size(haystack)) ? FALSE : TRUE;
401
0
}
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
0
               const char *default_value) {
408
0
  *value = default_value ? apr_pstrdup(pool, default_value) : NULL;
409
0
  if (json != NULL) {
410
0
    const oidc_json_t *v = oidc_json_object_get(json, name);
411
0
    if ((v != NULL) && (oidc_json_is_string(v))) {
412
0
      *value = apr_pstrdup(pool, oidc_json_string_value(v));
413
0
    }
414
0
  }
415
0
  return TRUE;
416
0
}
417
418
/*
419
 * get (optional) string array from a JSON object
420
 */
421
0
static void oidc_json_string_array_append(apr_pool_t *pool, const oidc_json_t *arr, apr_array_header_t *value) {
422
0
  for (size_t i = 0; i < oidc_json_array_size(arr); i++) {
423
0
    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
0
    if (oidc_json_is_string(v))
427
0
      APR_ARRAY_PUSH(value, const char *) = apr_pstrdup(pool, oidc_json_string_value(v));
428
0
  }
429
0
}
430
431
apr_byte_t oidc_json_object_get_string_array(apr_pool_t *pool, const oidc_json_t *json, const char *name,
432
0
               apr_array_header_t **value, const apr_array_header_t *default_value) {
433
0
  const oidc_json_t *arr = NULL;
434
0
  *value = (default_value != NULL) ? apr_array_copy(pool, default_value) : NULL;
435
0
  if (json != NULL) {
436
0
    arr = oidc_json_object_get(json, name);
437
0
    if ((arr != NULL) && (oidc_json_is_array(arr))) {
438
0
      *value = apr_array_make(pool, (int)oidc_json_array_size(arr), sizeof(const char *));
439
0
      oidc_json_string_array_append(pool, arr, *value);
440
0
    }
441
0
  }
442
0
  return TRUE;
443
0
}
444
445
/*
446
 * get (optional) int from a JSON object
447
 */
448
0
apr_byte_t oidc_json_object_get_int(const oidc_json_t *json, const char *name, int *value, const int default_value) {
449
0
  const oidc_json_t *v = NULL;
450
0
  *value = default_value;
451
0
  if (json != NULL) {
452
0
    v = oidc_json_object_get(json, name);
453
0
    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
0
      oidc_json_int_t n = oidc_json_integer_value(v);
456
0
      if (n > INT_MAX)
457
0
        *value = INT_MAX;
458
0
      else if (n < INT_MIN)
459
0
        *value = INT_MIN;
460
0
      else
461
0
        *value = (int)n;
462
0
      return TRUE;
463
0
    }
464
0
  }
465
0
  return FALSE;
466
0
}
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
0
apr_byte_t oidc_json_object_get_bool(const oidc_json_t *json, const char *name, int *value, const int default_value) {
490
0
  const oidc_json_t *v = NULL;
491
0
  *value = default_value;
492
0
  if (json != NULL) {
493
0
    v = oidc_json_object_get(json, name);
494
0
    if ((v != NULL) && (oidc_json_is_boolean(v))) {
495
0
      *value = oidc_json_is_true(v);
496
0
      return TRUE;
497
0
    }
498
0
  }
499
0
  return FALSE;
500
0
}
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
}