Coverage Report

Created: 2026-02-14 06:04

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