Coverage Report

Created: 2023-06-07 06:47

/src/sudo/lib/eventlog/parse_json.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2020-2023 Todd C. Miller <Todd.Miller@sudo.ws>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
/*
20
 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
21
 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
22
 */
23
24
#include <config.h>
25
26
#include <stdio.h>
27
#include <stdlib.h>
28
#ifdef HAVE_STDBOOL_H
29
# include <stdbool.h>
30
#else
31
# include "compat/stdbool.h"
32
#endif /* HAVE_STDBOOL_H */
33
#include <string.h>
34
#include <unistd.h>
35
#include <ctype.h>
36
#include <limits.h>
37
#include <fcntl.h>
38
#include <time.h>
39
40
#include "sudo_compat.h"
41
#include "sudo_debug.h"
42
#include "sudo_eventlog.h"
43
#include "sudo_fatal.h"
44
#include "sudo_gettext.h"
45
#include "sudo_util.h"
46
47
#include "parse_json.h"
48
49
struct json_stack {
50
    unsigned int depth;
51
    unsigned int maxdepth;
52
    struct eventlog_json_object *frames[64];
53
};
54
2.83k
#define JSON_STACK_INTIALIZER(s) { 0, nitems((s).frames) };
55
56
static char *iolog_file;
57
58
static bool
59
json_store_columns(struct json_item *item, struct eventlog *evlog)
60
381
{
61
381
    debug_decl(json_store_columns, SUDO_DEBUG_UTIL);
62
63
381
    if (item->u.number < 1 || item->u.number > INT_MAX) {
64
122
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
65
122
      "tty cols %lld: out of range", item->u.number);
66
122
  evlog->columns = 0;
67
122
  debug_return_bool(false);
68
122
    }
69
70
259
    evlog->columns = item->u.number;
71
259
    debug_return_bool(true);
72
259
}
73
74
static bool
75
json_store_command(struct json_item *item, struct eventlog *evlog)
76
194
{
77
194
    debug_decl(json_store_command, SUDO_DEBUG_UTIL);
78
79
    /*
80
     * Note: struct eventlog must store command + args.
81
     *       We don't have argv yet so we append the args later.
82
     */
83
194
    free(evlog->command);
84
194
    evlog->command = item->u.string;
85
194
    item->u.string = NULL;
86
194
    debug_return_bool(true);
87
194
}
88
89
static bool
90
json_store_dumped_core(struct json_item *item, struct eventlog *evlog)
91
194
{
92
194
    debug_decl(json_store_dumped_core, SUDO_DEBUG_UTIL);
93
94
194
    evlog->dumped_core = item->u.boolean;
95
194
    debug_return_bool(true);
96
194
}
97
98
static bool
99
json_store_exit_value(struct json_item *item, struct eventlog *evlog)
100
361
{
101
361
    debug_decl(json_store_exit_value, SUDO_DEBUG_UTIL);
102
103
361
    if (item->u.number < 0 || item->u.number > INT_MAX) {
104
107
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
105
107
      "exit value %lld: out of range", item->u.number);
106
107
  evlog->exit_value = -1;
107
107
  debug_return_bool(false);
108
107
    }
109
110
254
    evlog->exit_value = item->u.number;
111
254
    debug_return_bool(true);
112
254
}
113
114
static bool
115
json_store_iolog_file(struct json_item *item, struct eventlog *evlog)
116
366
{
117
366
    debug_decl(json_store_iolog_file, SUDO_DEBUG_UTIL);
118
119
    /* Do set evlog->iolog_file directly, it is a substring of iolog_path. */
120
366
    free(iolog_file);
121
366
    iolog_file = item->u.string;
122
366
    item->u.string = NULL;
123
366
    debug_return_bool(true);
124
366
}
125
126
static bool
127
json_store_iolog_path(struct json_item *item, struct eventlog *evlog)
128
366
{
129
366
    debug_decl(json_store_iolog_path, SUDO_DEBUG_UTIL);
130
131
366
    free(evlog->iolog_path);
132
366
    evlog->iolog_path = item->u.string;
133
366
    item->u.string = NULL;
134
366
    debug_return_bool(true);
135
366
}
136
137
static bool
138
json_store_lines(struct json_item *item, struct eventlog *evlog)
139
382
{
140
382
    debug_decl(json_store_lines, SUDO_DEBUG_UTIL);
141
142
382
    if (item->u.number < 1 || item->u.number > INT_MAX) {
143
120
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
144
120
      "tty lines %lld: out of range", item->u.number);
145
120
  evlog->lines = 0;
146
120
  debug_return_bool(false);
147
120
    }
148
149
262
    evlog->lines = item->u.number;
150
262
    debug_return_bool(true);
151
262
}
152
153
static bool
154
json_store_peeraddr(struct json_item *item, struct eventlog *evlog)
155
195
{
156
195
    debug_decl(json_store_peeraddr, SUDO_DEBUG_UTIL);
157
158
195
    free(evlog->peeraddr);
159
195
    evlog->peeraddr = item->u.string;
160
195
    item->u.string = NULL;
161
195
    debug_return_bool(true);
162
195
}
163
164
static char **
165
json_array_to_strvec(struct eventlog_json_object *array)
166
2.05k
{
167
2.05k
    struct json_item *item;
168
2.05k
    size_t len = 0;
169
2.05k
    char **ret;
170
2.05k
    debug_decl(json_array_to_strvec, SUDO_DEBUG_UTIL);
171
172
526k
    TAILQ_FOREACH(item, &array->items, entries) {
173
  /* Can only convert arrays of string. */
174
526k
  if (item->type != JSON_STRING) {
175
2
      sudo_warnx(U_("expected JSON_STRING, got %d"), item->type);
176
2
      debug_return_ptr(NULL);
177
2
  }
178
  /* Prevent integer overflow. */
179
526k
  if (++len == INT_MAX) {
180
0
      sudo_warnx("%s", U_("JSON_ARRAY too large"));
181
0
      debug_return_ptr(NULL);
182
0
  }
183
526k
    }
184
2.05k
    if ((ret = reallocarray(NULL, len + 1, sizeof(char *))) == NULL) {
185
0
  sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
186
0
  debug_return_ptr(NULL);
187
0
    }
188
2.05k
    len = 0;
189
526k
    TAILQ_FOREACH(item, &array->items, entries) {
190
526k
  ret[len++] = item->u.string;
191
526k
  item->u.string = NULL;
192
526k
    }
193
2.05k
    ret[len] = NULL;
194
195
2.05k
    debug_return_ptr(ret);
196
2.05k
}
197
198
static bool
199
json_store_runargv(struct json_item *item, struct eventlog *evlog)
200
614
{
201
614
    int i;
202
614
    debug_decl(json_store_runargv, SUDO_DEBUG_UTIL);
203
204
614
    if (evlog->argv != NULL) {
205
972
  for (i = 0; evlog->argv[i] != NULL; i++)
206
396
      free(evlog->argv[i]);
207
576
  free(evlog->argv);
208
576
    }
209
614
    evlog->argv = json_array_to_strvec(&item->u.child);
210
211
614
    debug_return_bool(evlog->argv != NULL);
212
614
}
213
214
static bool
215
json_store_runenv(struct json_item *item, struct eventlog *evlog)
216
825
{
217
825
    int i;
218
825
    debug_decl(json_store_runenv, SUDO_DEBUG_UTIL);
219
220
825
    if (evlog->envp != NULL) {
221
1.34k
  for (i = 0; evlog->envp[i] != NULL; i++)
222
582
      free(evlog->envp[i]);
223
762
  free(evlog->envp);
224
762
    }
225
825
    evlog->envp = json_array_to_strvec(&item->u.child);
226
227
825
    debug_return_bool(evlog->envp != NULL);
228
825
}
229
230
static bool
231
json_store_runenv_override(struct json_item *item, struct eventlog *evlog)
232
614
{
233
614
    int i;
234
614
    debug_decl(json_store_runenv_override, SUDO_DEBUG_UTIL);
235
236
614
    if (evlog->env_add != NULL) {
237
972
  for (i = 0; evlog->env_add[i] != NULL; i++)
238
396
      free(evlog->env_add[i]);
239
576
  free(evlog->env_add);
240
576
    }
241
614
    evlog->env_add = json_array_to_strvec(&item->u.child);
242
243
614
    debug_return_bool(evlog->env_add != NULL);
244
614
}
245
246
static bool
247
json_store_rungid(struct json_item *item, struct eventlog *evlog)
248
194
{
249
194
    debug_decl(json_store_rungid, SUDO_DEBUG_UTIL);
250
251
194
    evlog->rungid = (gid_t)item->u.number;
252
194
    debug_return_bool(true);
253
194
}
254
255
static bool
256
json_store_rungroup(struct json_item *item, struct eventlog *evlog)
257
194
{
258
194
    debug_decl(json_store_rungroup, SUDO_DEBUG_UTIL);
259
260
194
    free(evlog->rungroup);
261
194
    evlog->rungroup = item->u.string;
262
194
    item->u.string = NULL;
263
194
    debug_return_bool(true);
264
194
}
265
266
static bool
267
json_store_runuid(struct json_item *item, struct eventlog *evlog)
268
194
{
269
194
    debug_decl(json_store_runuid, SUDO_DEBUG_UTIL);
270
271
194
    evlog->runuid = (uid_t)item->u.number;
272
194
    debug_return_bool(true);
273
194
}
274
275
static bool
276
json_store_runuser(struct json_item *item, struct eventlog *evlog)
277
194
{
278
194
    debug_decl(json_store_runuser, SUDO_DEBUG_UTIL);
279
280
194
    free(evlog->runuser);
281
194
    evlog->runuser = item->u.string;
282
194
    item->u.string = NULL;
283
194
    debug_return_bool(true);
284
194
}
285
286
static bool
287
json_store_runchroot(struct json_item *item, struct eventlog *evlog)
288
194
{
289
194
    debug_decl(json_store_runchroot, SUDO_DEBUG_UTIL);
290
291
194
    free(evlog->runchroot);
292
194
    evlog->runchroot = item->u.string;
293
194
    item->u.string = NULL;
294
194
    debug_return_bool(true);
295
194
}
296
297
static bool
298
json_store_runcwd(struct json_item *item, struct eventlog *evlog)
299
194
{
300
194
    debug_decl(json_store_runcwd, SUDO_DEBUG_UTIL);
301
302
194
    free(evlog->runcwd);
303
194
    evlog->runcwd = item->u.string;
304
194
    item->u.string = NULL;
305
194
    debug_return_bool(true);
306
194
}
307
308
static bool
309
json_store_signal(struct json_item *item, struct eventlog *evlog)
310
194
{
311
194
    debug_decl(json_store_signal, SUDO_DEBUG_UTIL);
312
313
194
    free(evlog->signal_name);
314
194
    evlog->signal_name = item->u.string;
315
194
    item->u.string = NULL;
316
194
    debug_return_bool(true);
317
194
}
318
319
static bool
320
json_store_submitcwd(struct json_item *item, struct eventlog *evlog)
321
194
{
322
194
    debug_decl(json_store_submitcwd, SUDO_DEBUG_UTIL);
323
324
194
    free(evlog->cwd);
325
194
    evlog->cwd = item->u.string;
326
194
    item->u.string = NULL;
327
194
    debug_return_bool(true);
328
194
}
329
330
static bool
331
json_store_submithost(struct json_item *item, struct eventlog *evlog)
332
194
{
333
194
    debug_decl(json_store_submithost, SUDO_DEBUG_UTIL);
334
335
194
    free(evlog->submithost);
336
194
    evlog->submithost = item->u.string;
337
194
    item->u.string = NULL;
338
194
    debug_return_bool(true);
339
194
}
340
341
static bool
342
json_store_submituser(struct json_item *item, struct eventlog *evlog)
343
194
{
344
194
    debug_decl(json_store_submituser, SUDO_DEBUG_UTIL);
345
346
194
    free(evlog->submituser);
347
194
    evlog->submituser = item->u.string;
348
194
    item->u.string = NULL;
349
194
    debug_return_bool(true);
350
194
}
351
352
static bool
353
json_store_submitgroup(struct json_item *item, struct eventlog *evlog)
354
194
{
355
194
    debug_decl(json_store_submitgroup, SUDO_DEBUG_UTIL);
356
357
194
    free(evlog->submitgroup);
358
194
    evlog->submitgroup = item->u.string;
359
194
    item->u.string = NULL;
360
194
    debug_return_bool(true);
361
194
}
362
363
static bool
364
json_store_timespec(struct json_item *item, struct timespec *ts)
365
970
{
366
970
    struct eventlog_json_object *object;
367
970
    debug_decl(json_store_timespec, SUDO_DEBUG_UTIL);
368
369
970
    object = &item->u.child;
370
1.32k
    TAILQ_FOREACH(item, &object->items, entries) {
371
1.32k
  if (item->type != JSON_NUMBER)
372
582
      continue;
373
738
  if (strcmp(item->name, "seconds") == 0) {
374
194
      ts->tv_sec = item->u.number;
375
194
      continue;
376
194
  }
377
544
  if (strcmp(item->name, "nanoseconds") == 0) {
378
194
      ts->tv_nsec = item->u.number;
379
194
      continue;
380
194
  }
381
544
    }
382
970
    debug_return_bool(true);
383
970
}
384
385
static bool
386
json_store_iolog_offset(struct json_item *item, struct eventlog *evlog)
387
194
{
388
194
    return json_store_timespec(item, &evlog->iolog_offset);
389
194
}
390
391
static bool
392
json_store_run_time(struct json_item *item, struct eventlog *evlog)
393
558
{
394
558
    return json_store_timespec(item, &evlog->run_time);
395
558
}
396
397
static bool
398
json_store_timestamp(struct json_item *item, struct eventlog *evlog)
399
218
{
400
218
    return json_store_timespec(item, &evlog->submit_time);
401
218
}
402
403
static bool
404
json_store_ttyname(struct json_item *item, struct eventlog *evlog)
405
194
{
406
194
    debug_decl(json_store_ttyname, SUDO_DEBUG_UTIL);
407
408
194
    free(evlog->ttyname);
409
194
    evlog->ttyname = item->u.string;
410
194
    item->u.string = NULL;
411
194
    debug_return_bool(true);
412
194
}
413
414
static bool
415
json_store_uuid(struct json_item *item, struct eventlog *evlog)
416
414
{
417
414
    bool ret = false;
418
414
    debug_decl(json_store_uuid, SUDO_DEBUG_UTIL);
419
420
414
    if (strlen(item->u.string) == sizeof(evlog->uuid_str) - 1) {
421
380
  memcpy(evlog->uuid_str, item->u.string, sizeof(evlog->uuid_str));
422
380
  ret = true;
423
380
    }
424
414
    free(item->u.string);
425
414
    item->u.string = NULL;
426
414
    debug_return_bool(ret);
427
414
}
428
429
static struct evlog_json_key {
430
    const char *name;
431
    enum json_value_type type;
432
    bool (*setter)(struct json_item *, struct eventlog *);
433
} evlog_json_keys[] = {
434
    { "columns", JSON_NUMBER, json_store_columns },
435
    { "command", JSON_STRING, json_store_command },
436
    { "dumped_core", JSON_BOOL, json_store_dumped_core },
437
    { "exit_value", JSON_NUMBER, json_store_exit_value },
438
    { "iolog_file", JSON_STRING, json_store_iolog_file },
439
    { "iolog_path", JSON_STRING, json_store_iolog_path },
440
    { "iolog_offset", JSON_OBJECT, json_store_iolog_offset },
441
    { "lines", JSON_NUMBER, json_store_lines },
442
    { "peeraddr", JSON_STRING, json_store_peeraddr },
443
    { "run_time", JSON_OBJECT, json_store_run_time },
444
    { "runargv", JSON_ARRAY, json_store_runargv },
445
    { "runenv", JSON_ARRAY, json_store_runenv },
446
    { "runenv_override", JSON_ARRAY, json_store_runenv_override },
447
    { "rungid", JSON_ID, json_store_rungid },
448
    { "rungroup", JSON_STRING, json_store_rungroup },
449
    { "runuid", JSON_ID, json_store_runuid },
450
    { "runuser", JSON_STRING, json_store_runuser },
451
    { "runchroot", JSON_STRING, json_store_runchroot },
452
    { "runcwd", JSON_STRING, json_store_runcwd },
453
    { "signal", JSON_STRING, json_store_signal },
454
    { "submitcwd", JSON_STRING, json_store_submitcwd },
455
    { "submithost", JSON_STRING, json_store_submithost },
456
    { "submitgroup", JSON_STRING, json_store_submitgroup },
457
    { "submituser", JSON_STRING, json_store_submituser },
458
    { "timestamp", JSON_OBJECT, json_store_timestamp },
459
    { "ttyname", JSON_STRING, json_store_ttyname },
460
    { "uuid", JSON_STRING, json_store_uuid },
461
    { NULL }
462
};
463
464
static struct json_item *
465
new_json_item(enum json_value_type type, char *name, unsigned int lineno)
466
554k
{
467
554k
    struct json_item *item;
468
554k
    debug_decl(new_json_item, SUDO_DEBUG_UTIL);
469
470
554k
    if ((item = malloc(sizeof(*item))) == NULL)  {
471
0
  sudo_warnx(U_("%s: %s"), __func__,
472
0
      U_("unable to allocate memory"));
473
0
  debug_return_ptr(NULL);
474
0
    }
475
554k
    item->name = name;
476
554k
    item->type = type;
477
554k
    item->lineno = lineno;
478
479
554k
    debug_return_ptr(item);
480
554k
}
481
482
static char *
483
json_parse_string(char **strp)
484
542k
{
485
542k
    char *dst, *end, *ret, *src = *strp + 1;
486
542k
    size_t len;
487
542k
    debug_decl(json_parse_string, SUDO_DEBUG_UTIL);
488
489
8.33M
    for (end = src; *end != '"' && *end != '\0'; end++) {
490
7.79M
  if (end[0] == '\\' && end[1] == '"')
491
235
      end++;
492
7.79M
    }
493
542k
    if (*end != '"') {
494
80
  sudo_warnx("%s", U_("missing double quote in name"));
495
80
  debug_return_str(NULL);
496
80
    }
497
542k
    len = (size_t)(end - src);
498
499
    /* Copy string, flattening escaped chars. */
500
542k
    dst = ret = malloc(len + 1);
501
542k
    if (dst == NULL) {
502
0
  sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
503
0
  debug_return_str(NULL);
504
0
    }
505
8.22M
    while (src < end) {
506
7.68M
  char ch = *src++;
507
7.68M
  if (ch == '\\') {
508
28.1k
      switch (*src) {
509
195
      case 'b':
510
195
    ch = '\b';
511
195
    break;
512
498
      case 'f':
513
498
    ch = '\f';
514
498
    break;
515
251
      case 'n':
516
251
    ch = '\n';
517
251
    break;
518
282
      case 'r':
519
282
    ch = '\r';
520
282
    break;
521
777
      case 't':
522
777
    ch = '\t';
523
777
    break;
524
24.4k
      case 'u':
525
    /* Only currently handles 8-bit ASCII. */
526
24.4k
    if (src[1] == '0' && src[2] == '0') {
527
22.2k
        ch = sudo_hexchar(&src[3]);
528
22.2k
        if (ch != -1) {
529
19.1k
      src += 4;
530
19.1k
      break;
531
19.1k
        }
532
22.2k
    }
533
    /* Not in \u00XX format. */
534
24.4k
    FALLTHROUGH;
535
5.31k
      case '"':
536
5.71k
      case '\\':
537
6.96k
      default:
538
    /* Note: a bare \ at the end of a string will be removed. */
539
6.96k
    ch = *src;
540
6.96k
    break;
541
28.1k
      }
542
28.1k
      src++;
543
28.1k
  }
544
7.68M
  *dst++ = ch;
545
7.68M
    }
546
542k
    *dst = '\0';
547
548
    /* Trim trailing whitespace. */
549
542k
    do {
550
542k
  end++;
551
542k
    } while (isspace((unsigned char)*end));
552
542k
    *strp = end;
553
554
542k
    debug_return_str(ret);
555
542k
}
556
557
static void
558
free_json_items(struct json_item_list *items)
559
15.3k
{
560
15.3k
    struct json_item *item;
561
15.3k
    debug_decl(free_json_items, SUDO_DEBUG_UTIL);
562
563
569k
    while ((item = TAILQ_FIRST(items)) != NULL) {
564
554k
  TAILQ_REMOVE(items, item, entries);
565
554k
  switch (item->type) {
566
531k
  case JSON_STRING:
567
531k
      free(item->u.string);
568
531k
      break;
569
7.58k
  case JSON_ARRAY:
570
12.4k
  case JSON_OBJECT:
571
12.4k
      free_json_items(&item->u.child.items);
572
12.4k
      break;
573
0
  case JSON_ID:
574
7.01k
  case JSON_NUMBER:
575
9.63k
  case JSON_BOOL:
576
10.9k
  case JSON_NULL:
577
      /* Nothing to free. */
578
10.9k
      break;
579
0
  default:
580
0
      sudo_warnx("%s: internal error, invalid JSON type %d",
581
0
    __func__, item->type);
582
0
      break;
583
554k
  }
584
554k
  free(item->name);
585
554k
  free(item);
586
554k
    }
587
588
15.3k
    debug_return;
589
15.3k
}
590
591
void
592
eventlog_json_free(struct eventlog_json_object *root)
593
2.83k
{
594
2.83k
    debug_decl(eventlog_json_free, SUDO_DEBUG_UTIL);
595
2.83k
    if (root != NULL) {
596
2.83k
  free_json_items(&root->items);
597
2.83k
  free(root);
598
2.83k
    }
599
2.83k
    debug_return;
600
2.83k
}
601
602
bool
603
eventlog_json_parse(struct eventlog_json_object *object, struct eventlog *evlog)
604
1.50k
{
605
1.50k
    struct json_item *item;
606
1.50k
    bool ret = false;
607
1.50k
    debug_decl(eventlog_json_parse, SUDO_DEBUG_UTIL);
608
609
    /* First object holds all the actual data. */
610
1.50k
    item = TAILQ_FIRST(&object->items);
611
1.50k
    if (item == NULL) {
612
16
  sudo_warnx("%s", U_("missing JSON_OBJECT"));
613
16
  goto done;
614
16
    }
615
1.48k
    if (item->type != JSON_OBJECT) {
616
0
  sudo_warnx(U_("expected JSON_OBJECT, got %d"), item->type);
617
0
  goto done;
618
0
    }
619
1.48k
    object = &item->u.child;
620
621
8.85k
    TAILQ_FOREACH(item, &object->items, entries) {
622
8.85k
  struct evlog_json_key *key;
623
624
  /* expecting key:value pairs */
625
8.85k
  if (item->name == NULL) {
626
15
      sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
627
15
    "%s: missing object name", __func__);
628
15
      goto done;
629
15
  }
630
631
  /* lookup name */
632
122k
  for (key = evlog_json_keys; key->name != NULL; key++) {
633
122k
      if (strcmp(item->name, key->name) == 0)
634
8.21k
    break;
635
122k
  }
636
8.83k
  if (key->name == NULL) {
637
624
      sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
638
624
    "%s: unknown key %s", __func__, item->name);
639
8.21k
  } else if (key->type != item->type &&
640
8.21k
    (key->type != JSON_ID || item->type != JSON_NUMBER)) {
641
7
      sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
642
7
    "%s: key mismatch %s type %d, expected %d", __func__,
643
7
    item->name, item->type, key->type);
644
7
      goto done;
645
8.20k
  } else {
646
      /* Matched name and type. */
647
8.20k
      if (!key->setter(item, evlog)) {
648
385
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
649
385
        "unable to store %s", key->name);
650
385
    goto done;
651
385
      }
652
8.20k
  }
653
8.83k
    }
654
655
    /*
656
     * iolog_file must be a substring of iolog_path.
657
     */
658
1.07k
    if (iolog_file != NULL && evlog->iolog_path != NULL) {
659
172
  const size_t filelen = strlen(iolog_file);
660
172
  const size_t pathlen = strlen(evlog->iolog_path);
661
172
  if (filelen <= pathlen) {
662
170
      const char *cp = &evlog->iolog_path[pathlen - filelen];
663
170
      if (strcmp(cp, iolog_file) == 0) {
664
25
    evlog->iolog_file = cp;
665
25
      }
666
170
  }
667
172
    }
668
669
1.07k
    ret = true;
670
671
1.50k
done:
672
1.50k
    free(iolog_file);
673
1.50k
    iolog_file = NULL;
674
675
1.50k
    debug_return_bool(ret);
676
1.50k
}
677
678
static bool
679
json_insert_bool(struct json_item_list *items, char *name, bool value,
680
    unsigned int lineno)
681
2.62k
{
682
2.62k
    struct json_item *item;
683
2.62k
    debug_decl(json_insert_bool, SUDO_DEBUG_UTIL);
684
685
2.62k
    if ((item = new_json_item(JSON_BOOL, name, lineno)) == NULL)
686
0
  debug_return_bool(false);
687
2.62k
    item->u.boolean = value;
688
2.62k
    TAILQ_INSERT_TAIL(items, item, entries);
689
690
2.62k
    debug_return_bool(true);
691
2.62k
}
692
693
static bool
694
json_insert_null(struct json_item_list *items, char *name, unsigned int lineno)
695
1.34k
{
696
1.34k
    struct json_item *item;
697
1.34k
    debug_decl(json_insert_null, SUDO_DEBUG_UTIL);
698
699
1.34k
    if ((item = new_json_item(JSON_NULL, name, lineno)) == NULL)
700
0
  debug_return_bool(false);
701
1.34k
    TAILQ_INSERT_TAIL(items, item, entries);
702
703
1.34k
    debug_return_bool(true);
704
1.34k
}
705
706
static bool
707
json_insert_num(struct json_item_list *items, char *name, long long value,
708
    unsigned int lineno)
709
7.01k
{
710
7.01k
    struct json_item *item;
711
7.01k
    debug_decl(json_insert_num, SUDO_DEBUG_UTIL);
712
713
7.01k
    if ((item = new_json_item(JSON_NUMBER, name, lineno)) == NULL)
714
0
  debug_return_bool(false);
715
7.01k
    item->u.number = value;
716
7.01k
    TAILQ_INSERT_TAIL(items, item, entries);
717
718
7.01k
    debug_return_bool(true);
719
7.01k
}
720
721
static bool
722
json_insert_str(struct json_item_list *items, char *name, char **strp,
723
    unsigned int lineno)
724
531k
{
725
531k
    struct json_item *item;
726
531k
    debug_decl(json_insert_str, SUDO_DEBUG_UTIL);
727
728
531k
    if ((item = new_json_item(JSON_STRING, name, lineno)) == NULL)
729
0
  debug_return_bool(false);
730
531k
    item->u.string = json_parse_string(strp);
731
531k
    if (item->u.string == NULL) {
732
21
  free(item);
733
21
  debug_return_bool(false);
734
21
    }
735
531k
    TAILQ_INSERT_TAIL(items, item, entries);
736
737
531k
    debug_return_bool(true);
738
531k
}
739
740
static struct eventlog_json_object *
741
json_stack_push(struct json_stack *stack, struct json_item_list *items,
742
    struct eventlog_json_object *frame, enum json_value_type type, char *name,
743
    unsigned int lineno)
744
12.4k
{
745
12.4k
    struct json_item *item;
746
12.4k
    debug_decl(json_stack_push, SUDO_DEBUG_UTIL);
747
748
    /* We limit the stack size rather than expanding it. */
749
12.4k
    if (stack->depth >= stack->maxdepth) {
750
9
  sudo_warnx(U_("json stack exhausted (max %u frames)"), stack->maxdepth);
751
9
  debug_return_ptr(NULL);
752
9
    }
753
754
    /* Allocate a new item and insert it into the list. */
755
12.4k
    if ((item = new_json_item(type, name, lineno)) == NULL)
756
0
  debug_return_ptr(NULL);
757
12.4k
    TAILQ_INIT(&item->u.child.items);
758
12.4k
    item->u.child.parent = item;
759
12.4k
    TAILQ_INSERT_TAIL(items, item, entries);
760
761
    /* Push the current frame onto the stack (depth check performed above). */
762
12.4k
    stack->frames[stack->depth++] = frame;
763
764
    /* Return the new frame */
765
12.4k
    debug_return_ptr(&item->u.child);
766
12.4k
}
767
768
/* Only expect a value if a name is defined or we are in an array. */
769
553k
#define expect_value (name != NULL || (frame->parent != NULL && frame->parent->type == JSON_ARRAY))
770
771
struct eventlog_json_object *
772
eventlog_json_read(FILE *fp, const char *filename)
773
2.83k
{
774
2.83k
    struct eventlog_json_object *frame, *root;
775
2.83k
    struct json_stack stack = JSON_STACK_INTIALIZER(stack);
776
2.83k
    unsigned int lineno = 0;
777
2.83k
    char *name = NULL;
778
2.83k
    char *cp, *buf = NULL;
779
2.83k
    size_t bufsize = 0;
780
2.83k
    ssize_t len;
781
2.83k
    bool saw_comma = false;
782
2.83k
    long long num;
783
2.83k
    char ch;
784
2.83k
    debug_decl(eventlog_json_read, SUDO_DEBUG_UTIL);
785
786
2.83k
    root = malloc(sizeof(*root));
787
2.83k
    if (root == NULL)
788
0
  goto bad;
789
790
2.83k
    root->parent = NULL;
791
2.83k
    TAILQ_INIT(&root->items);
792
793
2.83k
    frame = root;
794
5.76k
    while ((len = getdelim(&buf, &bufsize, '\n', fp)) != -1) {
795
3.64k
  char *ep = buf + len - 1;
796
3.64k
  cp = buf;
797
798
3.64k
  lineno++;
799
800
  /* Trim trailing whitespace. */
801
4.46k
  while (ep > cp && isspace((unsigned char)*ep))
802
821
      ep--;
803
3.64k
  ep[1] = '\0';
804
805
577k
  for (;;) {
806
577k
      const char *errstr;
807
808
      /* Trim leading whitespace, skip blank lines. */
809
577k
      while (isspace((unsigned char)*cp))
810
1.44k
    cp++;
811
812
      /* Check for comma separator and strip it out. */
813
577k
      if (*cp == ',') {
814
543k
    saw_comma = true;
815
543k
    cp++;
816
543k
    while (isspace((unsigned char)*cp))
817
194
        cp++;
818
543k
      }
819
820
      /* End of line? */
821
577k
      if (*cp == '\0')
822
2.93k
    break;
823
824
574k
      switch (*cp) {
825
4.90k
      case '{':
826
4.90k
    if (name == NULL && frame->parent != NULL) {
827
2
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
828
2
      U_("objects must consist of name:value pairs"));
829
2
        goto bad;
830
2
    }
831
4.90k
    if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
832
7
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
833
7
      U_("missing separator between values"));
834
7
        goto bad;
835
7
    }
836
4.90k
    cp++;
837
4.90k
    saw_comma = false;
838
4.90k
    frame = json_stack_push(&stack, &frame->items, frame,
839
4.90k
        JSON_OBJECT, name, lineno);
840
4.90k
    if (frame == NULL)
841
3
        goto bad;
842
4.89k
    name = NULL;
843
4.89k
    break;
844
3.34k
      case '}':
845
3.34k
    if (stack.depth == 0 || frame->parent == NULL ||
846
3.34k
      frame->parent->type != JSON_OBJECT) {
847
6
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
848
6
      U_("unmatched close brace"));
849
6
        goto bad;
850
6
    }
851
3.34k
    cp++;
852
3.34k
    frame = stack.frames[--stack.depth];
853
3.34k
    saw_comma = false;
854
3.34k
    break;
855
7.60k
      case '[':
856
7.60k
    if (frame->parent == NULL) {
857
        /* Must have an enclosing object. */
858
3
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
859
3
      U_("unexpected array"));
860
3
        goto bad;
861
3
    }
862
7.59k
    if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
863
2
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
864
2
      U_("missing separator between values"));
865
2
        goto bad;
866
2
    }
867
7.59k
    cp++;
868
7.59k
    saw_comma = false;
869
7.59k
    frame = json_stack_push(&stack, &frame->items, frame,
870
7.59k
        JSON_ARRAY, name, lineno);
871
7.59k
    if (frame == NULL)
872
6
        goto bad;
873
7.58k
    name = NULL;
874
7.58k
    break;
875
4.30k
      case ']':
876
4.30k
    if (stack.depth == 0 || frame->parent == NULL ||
877
4.30k
      frame->parent->type != JSON_ARRAY) {
878
2
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
879
2
      U_("unmatched close bracket"));
880
2
        goto bad;
881
2
    }
882
4.29k
    cp++;
883
4.29k
    frame = stack.frames[--stack.depth];
884
4.29k
    saw_comma = false;
885
4.29k
    break;
886
542k
      case '"':
887
542k
    if (frame->parent == NULL) {
888
        /* Must have an enclosing object. */
889
2
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
890
2
      U_("unexpected string"));
891
2
        goto bad;
892
2
    }
893
894
542k
    if (!expect_value) {
895
        /* Parse "name": */
896
11.4k
        if ((name = json_parse_string(&cp)) == NULL)
897
59
      goto bad;
898
        /* TODO: allow colon on next line? */
899
11.4k
        if (*cp != ':') {
900
208
      sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
901
208
          U_("missing colon after name"));
902
208
      goto bad;
903
208
        }
904
11.2k
        cp++;
905
531k
    } else {
906
531k
        if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
907
14
      sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
908
14
          U_("missing separator between values"));
909
14
      goto bad;
910
14
        }
911
531k
        saw_comma = false;
912
531k
        if (!json_insert_str(&frame->items, name, &cp, lineno))
913
21
      goto bad;
914
531k
        name = NULL;
915
531k
    }
916
542k
    break;
917
542k
      case 't':
918
1.46k
    if (strncmp(cp, "true", sizeof("true") - 1) != 0)
919
28
        goto parse_error;
920
1.43k
    if (!expect_value) {
921
2
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
922
2
      U_("unexpected boolean"));
923
2
        goto bad;
924
2
    }
925
1.43k
    cp += sizeof("true") - 1;
926
1.43k
    if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0')
927
11
        goto parse_error;
928
1.42k
    if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
929
16
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
930
16
      U_("missing separator between values"));
931
16
        goto bad;
932
16
    }
933
1.40k
    saw_comma = false;
934
935
1.40k
    if (!json_insert_bool(&frame->items, name, true, lineno))
936
0
        goto bad;
937
1.40k
    name = NULL;
938
1.40k
    break;
939
1.28k
      case 'f':
940
1.28k
    if (strncmp(cp, "false", sizeof("false") - 1) != 0)
941
37
        goto parse_error;
942
1.25k
    if (!expect_value) {
943
3
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
944
3
      U_("unexpected boolean"));
945
3
        goto bad;
946
3
    }
947
1.24k
    cp += sizeof("false") - 1;
948
1.24k
    if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0')
949
15
        goto parse_error;
950
1.23k
    if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
951
17
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
952
17
      U_("missing separator between values"));
953
17
        goto bad;
954
17
    }
955
1.21k
    saw_comma = false;
956
957
1.21k
    if (!json_insert_bool(&frame->items, name, false, lineno))
958
0
        goto bad;
959
1.21k
    name = NULL;
960
1.21k
    break;
961
1.40k
      case 'n':
962
1.40k
    if (strncmp(cp, "null", sizeof("null") - 1) != 0)
963
27
        goto parse_error;
964
1.37k
    if (!expect_value) {
965
2
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
966
2
      U_("unexpected null"));
967
2
        goto bad;
968
2
    }
969
1.37k
    cp += sizeof("null") - 1;
970
1.37k
    if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0')
971
14
        goto parse_error;
972
1.35k
    if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
973
15
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
974
15
      U_("missing separator between values"));
975
15
        goto bad;
976
15
    }
977
1.34k
    saw_comma = false;
978
979
1.34k
    if (!json_insert_null(&frame->items, name, lineno))
980
0
        goto bad;
981
1.34k
    name = NULL;
982
1.34k
    break;
983
3.89k
      case '+': case '-': case '0': case '1': case '2': case '3':
984
7.18k
      case '4': case '5': case '6': case '7': case '8': case '9':
985
7.18k
    if (!expect_value) {
986
16
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
987
16
      U_("unexpected number"));
988
16
        goto bad;
989
16
    }
990
    /* XXX - strtonumx() would be simpler here. */
991
7.16k
    len = strcspn(cp, " \f\n\r\t\v,");
992
7.16k
    ch = cp[len];
993
7.16k
    cp[len] = '\0';
994
7.16k
    if (!saw_comma && !TAILQ_EMPTY(&frame->items)) {
995
14
        sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, 
996
14
      U_("missing separator between values"));
997
14
        goto bad;
998
14
    }
999
7.15k
    saw_comma = false;
1000
7.15k
    num = sudo_strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr);
1001
7.15k
    if (errstr != NULL) {
1002
137
        sudo_warnx("%s:%u:%td: %s: %s", filename, lineno, cp - buf,
1003
137
      cp, U_(errstr));
1004
137
        goto bad;
1005
137
    }
1006
7.01k
    cp += len;
1007
7.01k
    *cp = ch;
1008
1009
7.01k
    if (!json_insert_num(&frame->items, name, num, lineno))
1010
0
        goto bad;
1011
7.01k
    name = NULL;
1012
7.01k
    break;
1013
22
      default:
1014
22
    goto parse_error;
1015
574k
      }
1016
574k
  }
1017
3.64k
    }
1018
2.12k
    if (stack.depth != 0) {
1019
621
  frame = stack.frames[stack.depth - 1];
1020
621
  if (frame->parent == NULL || frame->parent->type == JSON_OBJECT) {
1021
543
      sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf,
1022
543
    U_("unmatched close brace"));
1023
543
  } else {
1024
78
      sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf,
1025
78
    U_("unmatched close bracket"));
1026
78
  }
1027
621
  goto bad;
1028
621
    }
1029
1030
1.50k
    goto done;
1031
1032
1.50k
parse_error:
1033
154
    sudo_warnx("%s:%u:%td: %s", filename, lineno, cp - buf, U_("parse error"));
1034
1.33k
bad:
1035
1.33k
    eventlog_json_free(root);
1036
1.33k
    root = NULL;
1037
2.83k
done:
1038
2.83k
    free(buf);
1039
2.83k
    free(name);
1040
1041
2.83k
    debug_return_ptr(root);
1042
2.83k
}