Coverage Report

Created: 2025-10-10 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/lib/util/sudo_debug.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2011-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
#include <config.h>
20
21
#include <sys/stat.h>
22
#include <sys/time.h>
23
#include <sys/uio.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#ifdef HAVE_STRINGS_H
28
# include <strings.h>
29
#endif /* HAVE_STRINGS_H */
30
#include <unistd.h>
31
#include <ctype.h>
32
#include <errno.h>
33
#include <fcntl.h>
34
#include <time.h>
35
36
#include <sudo_compat.h>
37
#include <sudo_conf.h>
38
#include <sudo_debug.h>
39
#include <sudo_fatal.h>
40
#include <sudo_gettext.h>
41
#include <sudo_plugin.h>
42
#include <sudo_util.h>
43
44
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
45
/*
46
 * The debug priorities and subsystems are currently hard-coded.
47
 * In the future we might consider allowing plugins to register their
48
 * own subsystems and provide direct access to the debugging API.
49
 */
50
51
/* Note: this must match the order in sudo_debug.h */
52
static const char *const sudo_debug_priorities[] = {
53
    "crit",
54
    "err",
55
    "warn",
56
    "notice",
57
    "diag",
58
    "info",
59
    "trace",
60
    "debug",
61
    NULL
62
};
63
64
/* Note: this must match the order in sudo_debug.h */
65
static const char *const sudo_debug_default_subsystems[] = {
66
    "args",
67
    "conv",
68
    "edit",
69
    "event",
70
    "exec",
71
    "hooks",
72
    "main",
73
    "netif",
74
    "pcomm",
75
    "plugin",
76
    "pty",
77
    "selinux",
78
    "util",
79
    "utmp",
80
    NULL
81
};
82
83
#define NUM_DEF_SUBSYSTEMS  (nitems(sudo_debug_default_subsystems) - 1)
84
85
/*
86
 * For multiple programs/plugins there is a per-program instance
87
 * and one or more outputs (files).
88
 */
89
struct sudo_debug_output {
90
    SLIST_ENTRY(sudo_debug_output) entries;
91
    char *filename;
92
    int *settings;
93
    int fd;
94
};
95
SLIST_HEAD(sudo_debug_output_list, sudo_debug_output);
96
struct sudo_debug_instance {
97
    char *program;
98
    const char *const *subsystems;
99
    const unsigned int *subsystem_ids;
100
    unsigned int max_subsystem;
101
    unsigned int refcnt;
102
    struct sudo_debug_output_list outputs;
103
};
104
105
/* Support up to 10 instances. */
106
#define SUDO_DEBUG_INSTANCE_MAX 10
107
static struct sudo_debug_instance *sudo_debug_instances[SUDO_DEBUG_INSTANCE_MAX];
108
static int sudo_debug_last_instance = -1;
109
110
static char sudo_debug_pidstr[STRLEN_MAX_SIGNED(int) + 3];
111
static size_t sudo_debug_pidlen;
112
113
#define round_nfds(_n)  (((_n) + (4 * NBBY) - 1) & ~((4 * NBBY) - 1))
114
static int sudo_debug_fds_size;
115
static unsigned char *sudo_debug_fds;
116
static int sudo_debug_max_fd = -1;
117
118
/* Default instance index to use for common utility functions. */
119
static int sudo_debug_active_instance = -1;
120
121
/*
122
 * Free the specified output structure.
123
 */
124
static void
125
sudo_debug_free_output(struct sudo_debug_output *output)
126
{
127
    free(output->filename);
128
    free(output->settings);
129
    if (output->fd != -1)
130
  close(output->fd);
131
    free(output);
132
}
133
134
/*
135
 * Create a new output file for the specified debug instance.
136
 * Returns NULL if the file cannot be opened or memory cannot be allocated.
137
 * XXX - check for duplicates
138
 */
139
static struct sudo_debug_output *
140
sudo_debug_new_output(struct sudo_debug_instance *instance,
141
    struct sudo_debug_file *debug_file, int minfd)
142
{
143
    char *buf, *cp, *last, *subsys, *pri;
144
    struct sudo_debug_output *output;
145
    unsigned int j;
146
    int i;
147
148
    /* Create new output for the instance. */
149
    /* XXX - reuse fd for existing filename? */
150
    output = calloc(1, sizeof(*output));
151
    if (output == NULL)
152
  goto oom;
153
    output->fd = -1;
154
    output->settings = reallocarray(NULL, instance->max_subsystem + 1,
155
  sizeof(int));
156
    if (output->settings == NULL)
157
  goto oom;
158
    output->filename = strdup(debug_file->debug_file);
159
    if (output->filename == NULL)
160
  goto oom;
161
162
    /* Init per-subsystems settings to -1 since 0 is a valid priority. */
163
    for (j = 0; j <= instance->max_subsystem; j++)
164
  output->settings[j] = -1;
165
166
    /* Open debug file. */
167
    output->fd = open(output->filename, O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
168
    if (output->fd == -1) {
169
  /* Create debug file as needed and set group ownership. */
170
  if (errno == ENOENT) {
171
      output->fd = open(output->filename, O_WRONLY|O_APPEND|O_CREAT,
172
    S_IRUSR|S_IWUSR);
173
  }
174
  if (output->fd == -1) {
175
      sudo_warn_nodebug("%s", output->filename);
176
      goto bad;
177
  }
178
  ignore_result(fchown(output->fd, (uid_t)-1, 0));
179
    }
180
    if (output->fd < minfd) {
181
  int newfd = fcntl(output->fd, F_DUPFD, minfd);
182
  if (newfd == -1) {
183
      sudo_warn_nodebug("%s", output->filename);
184
      goto bad;
185
  }
186
  close(output->fd);
187
  output->fd = newfd;
188
    }
189
    if (fcntl(output->fd, F_SETFD, FD_CLOEXEC) == -1) {
190
  sudo_warn_nodebug("%s", output->filename);
191
  goto bad;
192
    }
193
    if (sudo_debug_fds_size < output->fd) {
194
  /* Bump fds size to the next multiple of 4 * NBBY. */
195
  const int old_size = sudo_debug_fds_size / NBBY;
196
  const int new_size = round_nfds(output->fd + 1) / NBBY;
197
  unsigned char *new_fds;
198
199
  new_fds = realloc(sudo_debug_fds, (size_t)new_size);
200
  if (new_fds == NULL)
201
      goto oom;
202
  memset(new_fds + old_size, 0, (size_t)(new_size - old_size));
203
  sudo_debug_fds = new_fds;
204
  sudo_debug_fds_size = new_size * NBBY;
205
    }
206
    sudo_setbit(sudo_debug_fds, output->fd);
207
    if (output->fd > sudo_debug_max_fd)
208
  sudo_debug_max_fd = output->fd;
209
210
    /* Parse Debug conf string. */
211
    buf = strdup(debug_file->debug_flags);
212
    if (buf == NULL)
213
  goto oom;
214
    for ((cp = strtok_r(buf, ",", &last)); cp != NULL; (cp = strtok_r(NULL, ",", &last))) {
215
  /* Should be in the form subsys@pri. */
216
  subsys = cp;
217
  if ((pri = strchr(cp, '@')) == NULL)
218
      continue;
219
  *pri++ = '\0';
220
221
  /* Look up priority and subsystem, fill in sudo_debug_settings[]. */
222
  for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
223
      if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
224
    for (j = 0; instance->subsystems[j] != NULL; j++) {
225
        if (strcasecmp(subsys, "all") == 0) {
226
      const unsigned int idx = instance->subsystem_ids ?
227
          SUDO_DEBUG_SUBSYS(instance->subsystem_ids[j]) : j;
228
      if (i > output->settings[idx])
229
          output->settings[idx] = i;
230
      continue;
231
        }
232
        if (strcasecmp(subsys, instance->subsystems[j]) == 0) {
233
      const unsigned int idx = instance->subsystem_ids ?
234
          SUDO_DEBUG_SUBSYS(instance->subsystem_ids[j]) : j;
235
      if (i > output->settings[idx])
236
          output->settings[idx] = i;
237
      break;
238
        }
239
    }
240
    break;
241
      }
242
  }
243
    }
244
    free(buf);
245
246
    return output;
247
oom:
248
    // -V:sudo_warn_nodebug:575, 618
249
    sudo_warn_nodebug(NULL);
250
bad:
251
    if (output != NULL)
252
  sudo_debug_free_output(output);
253
    return NULL;
254
}
255
256
/*
257
 * Register a program/plugin with the debug framework,
258
 * parses settings string from sudo.conf and opens debug_files.
259
 * If subsystem names are specified they override the default values.
260
 * NOTE: subsystems must not be freed by caller unless deregistered.
261
 * Sets the active instance to the newly registered instance.
262
 * Returns instance index on success, SUDO_DEBUG_INSTANCE_INITIALIZER
263
 * if no debug files are specified and SUDO_DEBUG_INSTANCE_ERROR
264
 * on error.
265
 */
266
int
267
sudo_debug_register_v2(const char *program, const char *const subsystems[],
268
    unsigned int ids[], struct sudo_conf_debug_file_list *debug_files,
269
    int minfd)
270
{
271
    struct sudo_debug_instance *instance = NULL;
272
    struct sudo_debug_output *output;
273
    struct sudo_debug_file *debug_file;
274
    int idx, free_idx = -1;
275
    debug_decl_func(sudo_debug_register);
276
277
    if (debug_files == NULL)
278
  return SUDO_DEBUG_INSTANCE_INITIALIZER;
279
280
    /* Use default subsystem names if none are provided. */
281
    if (subsystems == NULL) {
282
  subsystems = sudo_debug_default_subsystems;
283
    } else if (ids == NULL) {
284
  /* If subsystems are specified we must have ids[] too. */
285
  return SUDO_DEBUG_INSTANCE_ERROR;
286
    }
287
288
    /* Search for existing instance. */
289
    for (idx = 0; idx <= sudo_debug_last_instance; idx++) {
290
  if (sudo_debug_instances[idx] == NULL) {
291
      free_idx = idx;
292
      continue;
293
  }
294
  if (sudo_debug_instances[idx]->subsystems == subsystems &&
295
      strcmp(sudo_debug_instances[idx]->program, program) == 0) {
296
      instance = sudo_debug_instances[idx];
297
      break;
298
  }
299
    }
300
301
    if (instance == NULL) {
302
  size_t i;
303
  unsigned int j, max_id = NUM_DEF_SUBSYSTEMS - 1;
304
305
  /* Fill in subsystem name -> id mapping as needed. */
306
  if (ids != NULL) {
307
      for (i = 0; subsystems[i] != NULL; i++) {
308
    /* Check default subsystems. */
309
    for (j = 0; j < NUM_DEF_SUBSYSTEMS; j++) {
310
        if (strcmp(subsystems[i], sudo_debug_default_subsystems[j]) == 0)
311
      break;
312
    }
313
    if (j == NUM_DEF_SUBSYSTEMS)
314
        j = ++max_id;
315
    ids[i] = ((j + 1) << 6);
316
      }
317
  }
318
319
  if (free_idx != -1)
320
      idx = free_idx;
321
  if (idx == SUDO_DEBUG_INSTANCE_MAX) {
322
      /* XXX - realloc? */
323
      sudo_warnx_nodebug("too many debug instances (max %d)", SUDO_DEBUG_INSTANCE_MAX);
324
      return SUDO_DEBUG_INSTANCE_ERROR;
325
  }
326
  if (idx != sudo_debug_last_instance + 1 && idx != free_idx) {
327
      sudo_warnx_nodebug("%s: instance number mismatch: expected %d or %d, got %d", __func__, sudo_debug_last_instance + 1, free_idx, idx);
328
      return SUDO_DEBUG_INSTANCE_ERROR;
329
  }
330
  if ((instance = malloc(sizeof(*instance))) == NULL)
331
      return SUDO_DEBUG_INSTANCE_ERROR;
332
  if ((instance->program = strdup(program)) == NULL) {
333
      free(instance);
334
      return SUDO_DEBUG_INSTANCE_ERROR;
335
  }
336
  instance->subsystems = subsystems;
337
  instance->subsystem_ids = ids;
338
  instance->max_subsystem = max_id;
339
  instance->refcnt = 1;
340
  SLIST_INIT(&instance->outputs);
341
  sudo_debug_instances[idx] = instance;
342
  if (idx != free_idx)
343
      sudo_debug_last_instance++;
344
    } else {
345
  /* Check for matching instance but different ids[]. */
346
  if (ids != NULL && instance->subsystem_ids != ids) {
347
      size_t i;
348
349
      for (i = 0; subsystems[i] != NULL; i++)
350
    ids[i] = instance->subsystem_ids[i];
351
  }
352
  instance->refcnt++;
353
    }
354
355
    TAILQ_FOREACH(debug_file, debug_files, entries) {
356
  output = sudo_debug_new_output(instance, debug_file, minfd);
357
  if (output != NULL)
358
      SLIST_INSERT_HEAD(&instance->outputs, output, entries);
359
    }
360
361
    /* Set active instance. */
362
    sudo_debug_active_instance = idx;
363
364
    /* Stash the pid string so we only have to format it once. */
365
    if (sudo_debug_pidlen == 0) {
366
  (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
367
      (int)getpid());
368
  sudo_debug_pidlen = strlen(sudo_debug_pidstr);
369
    }
370
371
    return idx;
372
}
373
374
int
375
sudo_debug_register_v1(const char *program, const char *const subsystems[],
376
    unsigned int ids[], struct sudo_conf_debug_file_list *debug_files)
377
{
378
    return sudo_debug_register_v2(program, subsystems, ids, debug_files, -1);
379
}
380
381
/*
382
 * De-register the specified instance from the debug subsystem
383
 * and free up any associated data structures.
384
 * Returns the number of remaining references for the instance or -1 on error.
385
 */
386
int
387
sudo_debug_deregister_v1(int idx)
388
{
389
    struct sudo_debug_instance *instance;
390
    struct sudo_debug_output *output, *next;
391
    debug_decl_func(sudo_debug_deregister);
392
393
    if (idx < 0 || idx > sudo_debug_last_instance) {
394
  sudo_warnx_nodebug("%s: invalid instance ID %d, max %d",
395
      __func__, idx, sudo_debug_last_instance);
396
  return -1;
397
    }
398
    /* Reset active instance as needed. */
399
    if (sudo_debug_active_instance == idx)
400
  sudo_debug_active_instance = -1;
401
402
    instance = sudo_debug_instances[idx];
403
    if (instance == NULL)
404
  return -1;      /* already deregistered */
405
406
    if (--instance->refcnt != 0)
407
  return (int)instance->refcnt; /* ref held by other caller */
408
409
    /* Free up instance data, note that subsystems[] is owned by caller. */
410
    sudo_debug_instances[idx] = NULL;
411
    SLIST_FOREACH_SAFE(output, &instance->outputs, entries, next) {
412
  close(output->fd);
413
  free(output->filename);
414
  free(output->settings);
415
  free(output);
416
    }
417
    free(instance->program);
418
    free(instance);
419
420
    if (idx == sudo_debug_last_instance)
421
  sudo_debug_last_instance--;
422
423
    return 0;
424
}
425
426
/*
427
 * Parse the "filename flags,..." debug_flags entry from sudo.conf
428
 * and insert a new sudo_debug_file struct into the list.
429
 * Returns 0 on success, 1 on parse error or -1 on malloc failure.
430
 */
431
int
432
sudo_debug_parse_flags_v1(struct sudo_conf_debug_file_list *debug_files,
433
    const char *entry)
434
{
435
    struct sudo_debug_file *debug_file;
436
    const char *filename, *flags;
437
    size_t namelen;
438
439
    /* Only process new-style debug flags: filename flags,... */
440
    filename = entry;
441
    if (*filename != '/' || (flags = strpbrk(filename, " \t")) == NULL)
442
  return 1;
443
    namelen = (size_t)(flags - filename);
444
    while (isblank((unsigned char)*flags))
445
  flags++;
446
    if (*flags != '\0') {
447
  if ((debug_file = calloc(1, sizeof(*debug_file))) == NULL)
448
      goto oom;
449
  if ((debug_file->debug_file = strndup(filename, namelen)) == NULL)
450
      goto oom;
451
  if ((debug_file->debug_flags = strdup(flags)) == NULL)
452
      goto oom;
453
  TAILQ_INSERT_TAIL(debug_files, debug_file, entries);
454
    }
455
    return 0;
456
oom:
457
    if (debug_file != NULL) {
458
  free(debug_file->debug_file);
459
  free(debug_file->debug_flags);
460
  free(debug_file);
461
    }
462
    return -1;
463
}
464
465
int
466
sudo_debug_get_instance_v1(const char *program)
467
{
468
    int idx;
469
470
    for (idx = 0; idx <= sudo_debug_last_instance; idx++) {
471
  if (sudo_debug_instances[idx] == NULL)
472
      continue;
473
  if (strcmp(sudo_debug_instances[idx]->program, program) == 0)
474
      return idx;
475
    }
476
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
477
}
478
479
pid_t
480
sudo_debug_fork_v1(void)
481
{
482
    pid_t pid;
483
484
    if ((pid = fork()) == 0) {
485
  (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
486
      (int)getpid());
487
  sudo_debug_pidlen = strlen(sudo_debug_pidstr);
488
    }
489
490
    return pid;
491
}
492
493
/* Functions versions of sudo_debug_enter* for backwards compatibility. */
494
void
495
sudo_debug_enter_v1(const char *func, const char *file, int line,
496
    unsigned int subsys)
497
{
498
    sudo_debug_enter(func, file, line, subsys);
499
}
500
501
void
502
sudo_debug_exit_v1(const char *func, const char *file, int line,
503
    unsigned int subsys)
504
{
505
    sudo_debug_exit(func, file, line, subsys);
506
}
507
508
void
509
sudo_debug_exit_int_v1(const char *func, const char *file, int line,
510
    unsigned int subsys, int ret)
511
{
512
    sudo_debug_exit_int(func, file, line, subsys, ret);
513
}
514
515
void
516
sudo_debug_exit_uint_v1(const char *func, const char *file, int line,
517
    unsigned int subsys, unsigned int ret)
518
{
519
    sudo_debug_exit_uint(func, file, line, subsys, ret);
520
}
521
522
void
523
sudo_debug_exit_long_v1(const char *func, const char *file, int line,
524
    unsigned int subsys, long ret)
525
{
526
    sudo_debug_exit_long(func, file, line, subsys, ret);
527
}
528
529
void
530
sudo_debug_exit_id_t_v1(const char *func, const char *file, int line,
531
    unsigned int subsys, id_t ret)
532
{
533
    sudo_debug_exit_id_t(func, file, line, subsys, ret);
534
}
535
536
void
537
sudo_debug_exit_size_t_v1(const char *func, const char *file, int line,
538
    unsigned int subsys, size_t ret)
539
{
540
    sudo_debug_exit_size_t(func, file, line, subsys, ret);
541
}
542
543
void
544
sudo_debug_exit_ssize_t_v1(const char *func, const char *file, int line,
545
    unsigned int subsys, ssize_t ret)
546
{
547
    sudo_debug_exit_ssize_t(func, file, line, subsys, ret);
548
}
549
550
void
551
sudo_debug_exit_time_t_v1(const char *func, const char *file, int line,
552
    unsigned int subsys, time_t ret)
553
{
554
    sudo_debug_exit_time_t(func, file, line, subsys, ret);
555
}
556
557
void
558
sudo_debug_exit_mode_t_v1(const char *func, const char *file, int line,
559
    unsigned int subsys, mode_t ret)
560
{
561
    sudo_debug_exit_mode_t(func, file, line, subsys, ret);
562
}
563
564
void
565
sudo_debug_exit_bool_v1(const char *func, const char *file, int line,
566
    unsigned int subsys, bool ret)
567
{
568
    sudo_debug_exit_bool(func, file, line, subsys, ret);
569
}
570
571
void
572
sudo_debug_exit_str_v1(const char *func, const char *file, int line,
573
    unsigned int subsys, const char *ret)
574
{
575
    sudo_debug_exit_str(func, file, line, subsys, ret);
576
}
577
578
void
579
sudo_debug_exit_str_masked_v1(const char *func, const char *file, int line,
580
    unsigned int subsys, const char *ret)
581
{
582
    sudo_debug_exit_str_masked(func, file, line, subsys, ret);
583
}
584
585
void
586
sudo_debug_exit_ptr_v1(const char *func, const char *file, int line,
587
    unsigned int subsys, const void *ret)
588
{
589
    sudo_debug_exit_ptr(func, file, line, subsys, ret);
590
}
591
592
void
593
sudo_debug_write2_v1(int fd, const char *func, const char *file, int lineno,
594
    const char *str, unsigned int len, int errnum)
595
{
596
    char numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
597
    char timebuf[64];
598
    struct timeval tv;
599
    struct iovec iov[12];
600
    int iovcnt = 3;
601
602
    /* Cannot use sudo_gettime_real() here since it calls sudo_debug. */
603
    timebuf[0] = '\0';
604
    if (gettimeofday(&tv, NULL) != -1) {
605
  time_t now = tv.tv_sec;
606
  struct tm tm;
607
  size_t tlen;
608
  if (localtime_r(&now, &tm) != NULL) {
609
      timebuf[sizeof(timebuf) - 1] = '\0';
610
      tlen = strftime(timebuf, sizeof(timebuf), "%b %e %H:%M:%S", &tm);
611
      if (tlen == 0 || timebuf[sizeof(timebuf) - 1] != '\0') {
612
    /* contents are undefined on error */
613
    timebuf[0] = '\0';
614
      } else {
615
    (void)snprintf(timebuf + tlen, sizeof(timebuf) - tlen,
616
        ".%03d ", (int)tv.tv_usec / 1000);
617
      }
618
  }
619
    }
620
    iov[0].iov_base = timebuf;
621
    iov[0].iov_len = strlen(timebuf);
622
623
    /* Prepend program name and pid with a trailing space. */
624
    iov[1].iov_base = (char *)getprogname();
625
    iov[1].iov_len = strlen(iov[1].iov_base);
626
    iov[2].iov_base = sudo_debug_pidstr;
627
    iov[2].iov_len = sudo_debug_pidlen;
628
629
    /* Add string, trimming any trailing newlines. */
630
    while (len > 0 && str[len - 1] == '\n')
631
  len--;
632
    if (len != 0) {
633
  iov[iovcnt].iov_base = (char *)str;
634
  iov[iovcnt].iov_len = len;
635
  iovcnt++;
636
    }
637
638
    /* Append error string if errno is specified. */
639
    if (errnum) {
640
  if (len != 0) {
641
      iov[iovcnt].iov_base = (char *)": ";
642
      iov[iovcnt].iov_len = 2;
643
      iovcnt++;
644
  }
645
  iov[iovcnt].iov_base = strerror(errnum);
646
  iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
647
  iovcnt++;
648
    }
649
650
    /* If function, file and lineno are specified, append them. */
651
    if (func != NULL && file != NULL && lineno != 0) {
652
  iov[iovcnt].iov_base = (char *)" @ ";
653
  iov[iovcnt].iov_len = 3;
654
  iovcnt++;
655
656
  iov[iovcnt].iov_base = (char *)func;
657
  iov[iovcnt].iov_len = strlen(func);
658
  iovcnt++;
659
660
  iov[iovcnt].iov_base = (char *)"() ";
661
  iov[iovcnt].iov_len = 3;
662
  iovcnt++;
663
664
  iov[iovcnt].iov_base = (char *)file;
665
  iov[iovcnt].iov_len = strlen(file);
666
  iovcnt++;
667
668
  (void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
669
  iov[iovcnt].iov_base = numbuf;
670
  iov[iovcnt].iov_len = strlen(numbuf);
671
  iovcnt++;
672
    }
673
674
    /* Append newline. */
675
    iov[iovcnt].iov_base = (char *)"\n";
676
    iov[iovcnt].iov_len = 1;
677
    iovcnt++;
678
679
    /* Write message in a single syscall */
680
    ignore_result(writev(fd, iov, iovcnt));
681
}
682
683
bool
684
sudo_debug_needed_v1(unsigned int level)
685
{
686
    unsigned int subsys;
687
    int pri;
688
    struct sudo_debug_instance *instance;
689
    struct sudo_debug_output *output;
690
    bool result = false;
691
692
    if (sudo_debug_active_instance == -1)
693
        goto out;
694
695
    /* Extract priority and subsystem from level. */
696
    pri = (int)SUDO_DEBUG_PRI(level);
697
    subsys = SUDO_DEBUG_SUBSYS(level);
698
699
    if (sudo_debug_active_instance > sudo_debug_last_instance)
700
        goto out;
701
702
    instance = sudo_debug_instances[sudo_debug_active_instance];
703
    if (instance == NULL)
704
        goto out;
705
706
    if (subsys <= instance->max_subsystem) {
707
        SLIST_FOREACH(output, &instance->outputs, entries) {
708
            if (output->settings[subsys] >= pri) {
709
                result = true;
710
                break;
711
            }
712
        }
713
    }
714
out:
715
    return result;
716
}
717
718
void
719
sudo_debug_vprintf2_v1(const char *func, const char *file, int lineno,
720
    unsigned int level, const char * restrict fmt, va_list ap)
721
{
722
    int pri, saved_errno = errno;
723
    unsigned int subsys;
724
    char static_buf[1024], *buf = static_buf;
725
    struct sudo_debug_instance *instance;
726
    struct sudo_debug_output *output;
727
    debug_decl_func(sudo_debug_vprintf2);
728
729
    if (sudo_debug_active_instance == -1)
730
  goto out;
731
732
    /* Extract priority and subsystem from level. */
733
    pri = (int)SUDO_DEBUG_PRI(level);
734
    subsys = SUDO_DEBUG_SUBSYS(level);
735
736
    /* Disable extra info if SUDO_DEBUG_LINENO is not enabled. */
737
    if (!ISSET(level, SUDO_DEBUG_LINENO)) {
738
  func = NULL;
739
  file = NULL;
740
  lineno = 0;
741
    }
742
743
    /* Find matching instance. */
744
    if (sudo_debug_active_instance > sudo_debug_last_instance) {
745
  sudo_warnx_nodebug("%s: invalid instance ID %d, max %d",
746
      __func__, sudo_debug_active_instance, sudo_debug_last_instance);
747
  goto out;
748
    }
749
    instance = sudo_debug_instances[sudo_debug_active_instance];
750
    if (instance == NULL) {
751
  sudo_warnx_nodebug("%s: unregistered instance index %d", __func__,
752
      sudo_debug_active_instance);
753
  goto out;
754
    }
755
756
    SLIST_FOREACH(output, &instance->outputs, entries) {
757
  /* Make sure we want debug info at this level. */
758
  if (subsys <= instance->max_subsystem && output->settings[subsys] >= pri) {
759
      int errcode, buflen = 0;
760
      va_list ap2;
761
762
      if (fmt != NULL) {
763
    /* Operate on a copy of ap to support multiple outputs. */
764
    va_copy(ap2, ap);
765
    buflen = vsnprintf(static_buf, sizeof(static_buf), fmt, ap2);
766
    va_end(ap2);
767
    if (buflen < 0) {
768
        sudo_warnx_nodebug("%s: invalid format string \"%s\"",
769
      __func__, fmt);
770
        buflen = 0;
771
    } else if (buflen >= ssizeof(static_buf)) {
772
        /* Not enough room in static buf, allocate dynamically. */
773
        va_copy(ap2, ap);
774
        buflen = vasprintf(&buf, fmt, ap2);
775
        if (buflen == -1) {
776
      buf = static_buf;
777
      buflen = (int)strlen(buf);
778
        }
779
        va_end(ap2);
780
    }
781
      }
782
      errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
783
      sudo_debug_write2(output->fd, func, file, lineno, buf,
784
    (unsigned int)buflen, errcode);
785
      if (buf != static_buf) {
786
    free(buf);
787
    buf = static_buf;
788
      }
789
  }
790
    }
791
out:
792
    errno = saved_errno;
793
}
794
795
#ifdef NO_VARIADIC_MACROS
796
void
797
sudo_debug_printf_nvm_v1(int pri, const char * restrict fmt, ...)
798
{
799
    va_list ap;
800
801
    va_start(ap, fmt);
802
    sudo_debug_vprintf2(NULL, NULL, 0, pri, fmt, ap);
803
    va_end(ap);
804
}
805
#endif /* NO_VARIADIC_MACROS */
806
807
void
808
sudo_debug_printf2_v1(const char *func, const char *file, int lineno,
809
    unsigned int level, const char * restrict fmt, ...)
810
{
811
    va_list ap;
812
813
    va_start(ap, fmt);
814
    sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
815
    va_end(ap);
816
}
817
818
#define EXEC_PREFIX "exec "
819
820
void
821
sudo_debug_execve2_v1(unsigned int level, const char *path, char *const argv[],
822
    char *const envp[])
823
{
824
    int pri, saved_errno = errno;
825
    unsigned int subsys;
826
    struct sudo_debug_instance *instance;
827
    struct sudo_debug_output *output;
828
    char * const *av;
829
    char *cp, static_buf[4096], *buf = static_buf;
830
    size_t buflen, plen;
831
    debug_decl_func(sudo_debug_execve2);
832
833
    if (sudo_debug_active_instance == -1 || path == NULL)
834
  goto out;
835
836
    /* Extract priority and subsystem from level. */
837
    pri = (int)SUDO_DEBUG_PRI(level);
838
    subsys = SUDO_DEBUG_SUBSYS(level);
839
840
    /* Find matching instance. */
841
    if (sudo_debug_active_instance > sudo_debug_last_instance) {
842
  sudo_warnx_nodebug("%s: invalid instance ID %d, max %d",
843
      __func__, sudo_debug_active_instance, sudo_debug_last_instance);
844
  goto out;
845
    }
846
    instance = sudo_debug_instances[sudo_debug_active_instance];
847
    if (instance == NULL) {
848
  sudo_warnx_nodebug("%s: unregistered instance index %d", __func__,
849
      sudo_debug_active_instance);
850
  goto out;
851
    }
852
    if (subsys > instance->max_subsystem)
853
  goto out;
854
855
    SLIST_FOREACH(output, &instance->outputs, entries) {
856
  bool log_envp = false;
857
858
  /* Make sure we want debug info at this level. */
859
  if (output->settings[subsys] < pri)
860
      continue;
861
862
  /* Log envp for debug level "debug". */
863
  if (output->settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp != NULL)
864
      log_envp = true;
865
866
  /* Alloc and build up buffer. */
867
  plen = strlen(path);
868
  buflen = sizeof(EXEC_PREFIX) -1 + plen;
869
  if (argv != NULL && argv[0] != NULL) {
870
      buflen += sizeof(" []") - 1;
871
      for (av = argv; *av; av++)
872
    buflen += strlen(*av) + 1;
873
      buflen--;
874
  }
875
  if (log_envp && envp[0] != NULL) {
876
      buflen += sizeof(" []") - 1;
877
      for (av = envp; *av; av++)
878
    buflen += strlen(*av) + 1;
879
      buflen--;
880
  }
881
  if (buflen >= sizeof(static_buf)) {
882
      buf = malloc(buflen + 1);
883
      if (buf == NULL)
884
    goto out;
885
  }
886
887
  /* Copy prefix and command. */
888
  memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
889
  cp = buf + sizeof(EXEC_PREFIX) - 1;
890
  memcpy(cp, path, plen);
891
  cp += plen;
892
893
  /* Copy argv. */
894
  if (argv != NULL && argv[0] != NULL) {
895
      *cp++ = ' ';
896
      *cp++ = '[';
897
      for (av = argv; *av; av++) {
898
    size_t avlen = strlen(*av);
899
    memcpy(cp, *av, avlen);
900
    cp += avlen;
901
    *cp++ = ' ';
902
      }
903
      cp[-1] = ']';
904
  }
905
906
  if (log_envp && envp[0] != NULL) {
907
      *cp++ = ' ';
908
      *cp++ = '[';
909
      for (av = envp; *av; av++) {
910
    size_t avlen = strlen(*av);
911
    memcpy(cp, *av, avlen);
912
    cp += avlen;
913
    *cp++ = ' ';
914
      }
915
      cp[-1] = ']';
916
  }
917
918
  *cp = '\0';
919
920
  sudo_debug_write(output->fd, buf, (unsigned int)buflen, 0);
921
  if (buf != static_buf) {
922
      free(buf);
923
      buf = static_buf;
924
  }
925
    }
926
out:
927
    errno = saved_errno;
928
}
929
930
/*
931
 * Returns the active instance or SUDO_DEBUG_INSTANCE_INITIALIZER
932
 * if no instance is active.
933
 */
934
int
935
sudo_debug_get_active_instance_v1(void)
936
{
937
    return sudo_debug_active_instance;
938
}
939
940
/*
941
 * Sets a new active instance, returning the old one.
942
 * Note that the old instance may be SUDO_DEBUG_INSTANCE_INITIALIZER
943
 * if this is the only instance.
944
 */
945
int
946
sudo_debug_set_active_instance_v1(int idx)
947
{
948
    const int old_idx = sudo_debug_active_instance;
949
950
    if (idx >= -1 && idx <= sudo_debug_last_instance)
951
  sudo_debug_active_instance = idx;
952
    return old_idx;
953
}
954
955
/*
956
 * Replace the ofd with nfd in all outputs if present.
957
 * Also updates sudo_debug_fds.
958
 */
959
void
960
sudo_debug_update_fd_v1(int ofd, int nfd)
961
{
962
    int idx;
963
964
    if (ofd <= sudo_debug_max_fd && sudo_isset(sudo_debug_fds, ofd)) {
965
  /* Update sudo_debug_fds. */
966
  sudo_clrbit(sudo_debug_fds, ofd);
967
  sudo_setbit(sudo_debug_fds, nfd);
968
969
  /* Update the outputs. */
970
  for (idx = 0; idx <= sudo_debug_last_instance; idx++) {
971
      struct sudo_debug_instance *instance;
972
      struct sudo_debug_output *output;
973
974
      instance = sudo_debug_instances[idx];
975
      if (instance == NULL)
976
    continue;
977
      SLIST_FOREACH(output, &instance->outputs, entries) {
978
    if (output->fd == ofd)
979
        output->fd = nfd;
980
      }
981
  }
982
    }
983
}
984
985
/*
986
 * Returns the highest debug output fd or -1 if no debug files open.
987
 * Fills in fds with the value of sudo_debug_fds.
988
 */
989
int
990
sudo_debug_get_fds_v1(unsigned char **fds)
991
{
992
    *fds = sudo_debug_fds;
993
    return sudo_debug_max_fd;
994
}
995
#else /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
996
int
997
sudo_debug_register_v2(const char *program, const char *const subsystems[],
998
    unsigned int ids[], struct sudo_conf_debug_file_list *debug_files,
999
    int minfd)
1000
0
{
1001
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1002
0
}
1003
1004
int
1005
sudo_debug_register_v1(const char *program, const char *const subsystems[],
1006
    unsigned int ids[], struct sudo_conf_debug_file_list *debug_files)
1007
0
{
1008
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1009
0
}
1010
1011
int
1012
sudo_debug_deregister_v1(int idx)
1013
0
{
1014
0
    return -1;
1015
0
}
1016
1017
int
1018
sudo_debug_parse_flags_v1(struct sudo_conf_debug_file_list *debug_files,
1019
    const char *entry)
1020
214
{
1021
214
    return 0;
1022
214
}
1023
1024
int
1025
sudo_debug_get_instance_v1(const char *program)
1026
0
{
1027
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1028
0
}
1029
1030
pid_t
1031
sudo_debug_fork_v1(void)
1032
0
{
1033
0
    return fork();
1034
0
}
1035
1036
void
1037
sudo_debug_enter_v1(const char *func, const char *file, int line,
1038
    unsigned int subsys)
1039
0
{
1040
0
}
1041
1042
void
1043
sudo_debug_exit_v1(const char *func, const char *file, int line,
1044
    unsigned int subsys)
1045
0
{
1046
0
}
1047
1048
void
1049
sudo_debug_exit_int_v1(const char *func, const char *file, int line,
1050
    unsigned int subsys, int ret)
1051
0
{
1052
0
}
1053
1054
void
1055
sudo_debug_exit_uint_v1(const char *func, const char *file, int line,
1056
    unsigned int subsys, unsigned int ret)
1057
0
{
1058
0
}
1059
1060
void
1061
sudo_debug_exit_long_v1(const char *func, const char *file, int line,
1062
    unsigned int subsys, long ret)
1063
0
{
1064
0
}
1065
1066
void
1067
sudo_debug_exit_id_t_v1(const char *func, const char *file, int line,
1068
    unsigned int subsys, id_t ret)
1069
0
{
1070
0
}
1071
1072
void
1073
sudo_debug_exit_size_t_v1(const char *func, const char *file, int line,
1074
    unsigned int subsys, size_t ret)
1075
0
{
1076
0
}
1077
1078
void
1079
sudo_debug_exit_ssize_t_v1(const char *func, const char *file, int line,
1080
    unsigned int subsys, ssize_t ret)
1081
0
{
1082
0
}
1083
1084
void
1085
sudo_debug_exit_time_t_v1(const char *func, const char *file, int line,
1086
    unsigned int subsys, time_t ret)
1087
0
{
1088
0
}
1089
1090
void
1091
sudo_debug_exit_mode_t_v1(const char *func, const char *file, int line,
1092
    unsigned int subsys, mode_t ret)
1093
0
{
1094
0
}
1095
1096
void
1097
sudo_debug_exit_bool_v1(const char *func, const char *file, int line,
1098
    unsigned int subsys, bool ret)
1099
0
{
1100
0
}
1101
1102
void
1103
sudo_debug_exit_str_v1(const char *func, const char *file, int line,
1104
    unsigned int subsys, const char *ret)
1105
0
{
1106
0
}
1107
1108
void
1109
sudo_debug_exit_str_masked_v1(const char *func, const char *file, int line,
1110
    unsigned int subsys, const char *ret)
1111
0
{
1112
0
}
1113
1114
void
1115
sudo_debug_exit_ptr_v1(const char *func, const char *file, int line,
1116
    unsigned int subsys, const void *ret)
1117
0
{
1118
0
}
1119
1120
void
1121
sudo_debug_write2_v1(int fd, const char *func, const char *file, int lineno,
1122
    const char *str, unsigned int len, int errnum)
1123
0
{
1124
0
}
1125
1126
bool
1127
sudo_debug_needed_v1(unsigned int level)
1128
0
{
1129
0
    return false;
1130
0
}
1131
1132
void
1133
sudo_debug_vprintf2_v1(const char *func, const char *file, int lineno,
1134
    unsigned int level, const char * restrict fmt, va_list ap)
1135
0
{
1136
0
}
1137
1138
#ifdef NO_VARIADIC_MACROS
1139
void
1140
sudo_debug_printf_nvm_v1(int pri, const char * restrict fmt, ...)
1141
{
1142
}
1143
#endif /* NO_VARIADIC_MACROS */
1144
1145
void
1146
sudo_debug_printf2_v1(const char *func, const char *file, int lineno,
1147
    unsigned int level, const char * restrict fmt, ...)
1148
5.60M
{
1149
5.60M
}
1150
1151
void
1152
sudo_debug_execve2_v1(unsigned int level, const char *path, char *const argv[],
1153
    char *const envp[])
1154
0
{
1155
0
}
1156
1157
int
1158
sudo_debug_get_active_instance_v1(void)
1159
0
{
1160
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1161
0
}
1162
1163
int
1164
sudo_debug_set_active_instance_v1(int idx)
1165
0
{
1166
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1167
0
}
1168
1169
void
1170
sudo_debug_update_fd_v1(int ofd, int nfd)
1171
0
{
1172
0
}
1173
1174
int
1175
sudo_debug_get_fds_v1(unsigned char **fds)
1176
0
{
1177
0
    return -1;
1178
0
}
1179
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */