Coverage Report

Created: 2025-07-11 06:58

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