Coverage Report

Created: 2023-06-07 06:47

/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-2017 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[(((sizeof(int) * 8) + 2) / 3) + 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, new_size);
205
  if (new_fds == NULL)
206
      goto oom;
207
  memset(new_fds + old_size, 0, 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
  unsigned int i, j, max_id = NUM_DEF_SUBSYSTEMS - 1;
308
309
  /* Fill in subsystem name -> id mapping as needed. */
310
  if (ids != NULL) {
311
      for (i = 0; subsystems[i] != NULL; i++) {
312
    /* Check default subsystems. */
313
    for (j = 0; j < NUM_DEF_SUBSYSTEMS; j++) {
314
        if (strcmp(subsystems[i], sudo_debug_default_subsystems[j]) == 0)
315
      break;
316
    }
317
    if (j == NUM_DEF_SUBSYSTEMS)
318
        j = ++max_id;
319
    ids[i] = ((j + 1) << 6);
320
      }
321
  }
322
323
  if (free_idx != -1)
324
      idx = free_idx;
325
  if (idx == SUDO_DEBUG_INSTANCE_MAX) {
326
      /* XXX - realloc? */
327
      sudo_warnx_nodebug("too many debug instances (max %d)", SUDO_DEBUG_INSTANCE_MAX);
328
      return SUDO_DEBUG_INSTANCE_ERROR;
329
  }
330
  if (idx != sudo_debug_last_instance + 1 && idx != free_idx) {
331
      sudo_warnx_nodebug("%s: instance number mismatch: expected %d or %d, got %d", __func__, sudo_debug_last_instance + 1, free_idx, idx);
332
      return SUDO_DEBUG_INSTANCE_ERROR;
333
  }
334
  if ((instance = malloc(sizeof(*instance))) == NULL)
335
      return SUDO_DEBUG_INSTANCE_ERROR;
336
  if ((instance->program = strdup(program)) == NULL) {
337
      free(instance);
338
      return SUDO_DEBUG_INSTANCE_ERROR;
339
  }
340
  instance->subsystems = subsystems;
341
  instance->subsystem_ids = ids;
342
  instance->max_subsystem = max_id;
343
  instance->refcnt = 1;
344
  SLIST_INIT(&instance->outputs);
345
  sudo_debug_instances[idx] = instance;
346
  if (idx != free_idx)
347
      sudo_debug_last_instance++;
348
    } else {
349
  /* Check for matching instance but different ids[]. */
350
  if (ids != NULL && instance->subsystem_ids != ids) {
351
      unsigned int i;
352
353
      for (i = 0; subsystems[i] != NULL; i++)
354
    ids[i] = instance->subsystem_ids[i];
355
  }
356
  instance->refcnt++;
357
    }
358
359
    TAILQ_FOREACH(debug_file, debug_files, entries) {
360
  output = sudo_debug_new_output(instance, debug_file, minfd);
361
  if (output != NULL)
362
      SLIST_INSERT_HEAD(&instance->outputs, output, entries);
363
    }
364
365
    /* Set active instance. */
366
    sudo_debug_active_instance = idx;
367
368
    /* Stash the pid string so we only have to format it once. */
369
    if (sudo_debug_pidlen == 0) {
370
  (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
371
      (int)getpid());
372
  sudo_debug_pidlen = strlen(sudo_debug_pidstr);
373
    }
374
375
    return idx;
376
}
377
378
int
379
sudo_debug_register_v1(const char *program, const char *const subsystems[],
380
    unsigned int ids[], struct sudo_conf_debug_file_list *debug_files)
381
{
382
    return sudo_debug_register_v2(program, subsystems, ids, debug_files, -1);
383
}
384
385
/*
386
 * De-register the specified instance from the debug subsystem
387
 * and free up any associated data structures.
388
 * Returns the number of remaining references for the instance or -1 on error.
389
 */
390
int
391
sudo_debug_deregister_v1(int idx)
392
{
393
    struct sudo_debug_instance *instance;
394
    struct sudo_debug_output *output, *next;
395
    debug_decl_func(sudo_debug_deregister);
396
397
    if (idx < 0 || idx > sudo_debug_last_instance) {
398
  sudo_warnx_nodebug("%s: invalid instance ID %d, max %d",
399
      __func__, idx, sudo_debug_last_instance);
400
  return -1;
401
    }
402
    /* Reset active instance as needed. */
403
    if (sudo_debug_active_instance == idx)
404
  sudo_debug_active_instance = -1;
405
406
    instance = sudo_debug_instances[idx];
407
    if (instance == NULL)
408
  return -1;      /* already deregistered */
409
410
    if (--instance->refcnt != 0)
411
  return instance->refcnt;  /* ref held by other caller */
412
413
    /* Free up instance data, note that subsystems[] is owned by caller. */
414
    sudo_debug_instances[idx] = NULL;
415
    SLIST_FOREACH_SAFE(output, &instance->outputs, entries, next) {
416
  close(output->fd);
417
  free(output->filename);
418
  free(output->settings);
419
  free(output);
420
    }
421
    free(instance->program);
422
    free(instance);
423
424
    if (idx == sudo_debug_last_instance)
425
  sudo_debug_last_instance--;
426
427
    return 0;
428
}
429
430
/*
431
 * Parse the "filename flags,..." debug_flags entry from sudo.conf
432
 * and insert a new sudo_debug_file struct into the list.
433
 * Returns 0 on success, 1 on parse error or -1 on malloc failure.
434
 */
435
int
436
sudo_debug_parse_flags_v1(struct sudo_conf_debug_file_list *debug_files,
437
    const char *entry)
438
{
439
    struct sudo_debug_file *debug_file;
440
    const char *filename, *flags;
441
    size_t namelen;
442
443
    /* Only process new-style debug flags: filename flags,... */
444
    filename = entry;
445
    if (*filename != '/' || (flags = strpbrk(filename, " \t")) == NULL)
446
  return 1;
447
    namelen = (size_t)(flags - filename);
448
    while (isblank((unsigned char)*flags))
449
  flags++;
450
    if (*flags != '\0') {
451
  if ((debug_file = calloc(1, sizeof(*debug_file))) == NULL)
452
      goto oom;
453
  if ((debug_file->debug_file = strndup(filename, namelen)) == NULL)
454
      goto oom;
455
  if ((debug_file->debug_flags = strdup(flags)) == NULL)
456
      goto oom;
457
  TAILQ_INSERT_TAIL(debug_files, debug_file, entries);
458
    }
459
    return 0;
460
oom:
461
    if (debug_file != NULL) {
462
  free(debug_file->debug_file);
463
  free(debug_file->debug_flags);
464
  free(debug_file);
465
    }
466
    return -1;
467
}
468
469
int
470
sudo_debug_get_instance_v1(const char *program)
471
{
472
    int idx;
473
474
    for (idx = 0; idx <= sudo_debug_last_instance; idx++) {
475
  if (sudo_debug_instances[idx] == NULL)
476
      continue;
477
  if (strcmp(sudo_debug_instances[idx]->program, program) == 0)
478
      return idx;
479
    }
480
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
481
}
482
483
pid_t
484
sudo_debug_fork_v1(void)
485
{
486
    pid_t pid;
487
488
    if ((pid = fork()) == 0) {
489
  (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
490
      (int)getpid());
491
  sudo_debug_pidlen = strlen(sudo_debug_pidstr);
492
    }
493
494
    return pid;
495
}
496
497
void
498
sudo_debug_enter_v1(const char *func, const char *file, int line,
499
    int subsys)
500
{
501
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
502
  "-> %s @ %s:%d", func, file, line);
503
}
504
505
void
506
sudo_debug_exit_v1(const char *func, const char *file, int line,
507
    int subsys)
508
{
509
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
510
  "<- %s @ %s:%d", func, file, line);
511
}
512
513
void
514
sudo_debug_exit_int_v1(const char *func, const char *file, int line,
515
    int subsys, int ret)
516
{
517
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
518
  "<- %s @ %s:%d := %d", func, file, line, ret);
519
}
520
521
void
522
sudo_debug_exit_long_v1(const char *func, const char *file, int line,
523
    int subsys, long ret)
524
{
525
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
526
  "<- %s @ %s:%d := %ld", func, file, line, ret);
527
}
528
529
void
530
sudo_debug_exit_id_t_v1(const char *func, const char *file, int line,
531
    int subsys, id_t ret)
532
{
533
#if SIZEOF_ID_T == 8
534
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
535
  "<- %s @ %s:%d := %lld", func, file, line, (long long)ret);
536
#else
537
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
538
  "<- %s @ %s:%d := %d", func, file, line, (int)ret);
539
#endif
540
}
541
542
void
543
sudo_debug_exit_size_t_v1(const char *func, const char *file, int line,
544
    int subsys, size_t ret)
545
{
546
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
547
  "<- %s @ %s:%d := %zu", func, file, line, ret);
548
}
549
550
void
551
sudo_debug_exit_ssize_t_v1(const char *func, const char *file, int line,
552
    int subsys, ssize_t ret)
553
{
554
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
555
  "<- %s @ %s:%d := %zd", func, file, line, ret);
556
}
557
558
void
559
sudo_debug_exit_time_t_v1(const char *func, const char *file, int line,
560
    int subsys, time_t ret)
561
{
562
#if SIZEOF_TIME_T == 8
563
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
564
  "<- %s @ %s:%d := %lld", func, file, line, (long long)ret);
565
#else
566
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
567
  "<- %s @ %s:%d := %d", func, file, line, (int)ret);
568
#endif
569
}
570
571
void
572
sudo_debug_exit_bool_v1(const char *func, const char *file, int line,
573
    int subsys, bool ret)
574
{
575
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
576
  "<- %s @ %s:%d := %s", func, file, line, ret ? "true" : "false");
577
}
578
579
void
580
sudo_debug_exit_str_v1(const char *func, const char *file, int line,
581
    int subsys, const char *ret)
582
{
583
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
584
  "<- %s @ %s:%d := %s", func, file, line, ret ? ret : "(null)");
585
}
586
587
void
588
sudo_debug_exit_str_masked_v1(const char *func, const char *file, int line,
589
    int subsys, const char *ret)
590
{
591
    static const char stars[] = "********************************************************************************";
592
    int len = ret ? strlen(ret) : sizeof("(null)") - 1;
593
594
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
595
  "<- %s @ %s:%d := %.*s", func, file, line, len, ret ? stars : "(null)");
596
}
597
598
void
599
sudo_debug_exit_ptr_v1(const char *func, const char *file, int line,
600
    int subsys, const void *ret)
601
{
602
    sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
603
  "<- %s @ %s:%d := %p", func, file, line, ret);
604
}
605
606
void
607
sudo_debug_write2_v1(int fd, const char *func, const char *file, int lineno,
608
    const char *str, int len, int errnum)
609
{
610
    char numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
611
    char timebuf[64];
612
    struct timeval tv;
613
    struct iovec iov[12];
614
    int iovcnt = 3;
615
616
    /* Cannot use sudo_gettime_real() here since it calls sudo_debug. */
617
    timebuf[0] = '\0';
618
    if (gettimeofday(&tv, NULL) != -1) {
619
  time_t now = tv.tv_sec;
620
  struct tm tm;
621
  size_t tlen;
622
  if (localtime_r(&now, &tm) != NULL) {
623
      timebuf[sizeof(timebuf) - 1] = '\0';
624
      tlen = strftime(timebuf, sizeof(timebuf), "%b %e %H:%M:%S", &tm);
625
      if (tlen == 0 || timebuf[sizeof(timebuf) - 1] != '\0') {
626
    /* contents are undefined on error */
627
    timebuf[0] = '\0';
628
      } else {
629
    (void)snprintf(timebuf + tlen, sizeof(timebuf) - tlen,
630
        ".%03d ", (int)tv.tv_usec / 1000);
631
      }
632
  }
633
    }
634
    iov[0].iov_base = timebuf;
635
    iov[0].iov_len = strlen(timebuf);
636
637
    /* Prepend program name and pid with a trailing space. */
638
    iov[1].iov_base = (char *)getprogname();
639
    iov[1].iov_len = strlen(iov[1].iov_base);
640
    iov[2].iov_base = sudo_debug_pidstr;
641
    iov[2].iov_len = sudo_debug_pidlen;
642
643
    /* Add string, trimming any trailing newlines. */
644
    while (len > 0 && str[len - 1] == '\n')
645
  len--;
646
    if (len > 0) {
647
  iov[iovcnt].iov_base = (char *)str;
648
  iov[iovcnt].iov_len = len;
649
  iovcnt++;
650
    }
651
652
    /* Append error string if errno is specified. */
653
    if (errnum) {
654
  if (len > 0) {
655
      iov[iovcnt].iov_base = (char *)": ";
656
      iov[iovcnt].iov_len = 2;
657
      iovcnt++;
658
  }
659
  iov[iovcnt].iov_base = strerror(errnum);
660
  iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
661
  iovcnt++;
662
    }
663
664
    /* If function, file and lineno are specified, append them. */
665
    if (func != NULL && file != NULL && lineno != 0) {
666
  iov[iovcnt].iov_base = (char *)" @ ";
667
  iov[iovcnt].iov_len = 3;
668
  iovcnt++;
669
670
  iov[iovcnt].iov_base = (char *)func;
671
  iov[iovcnt].iov_len = strlen(func);
672
  iovcnt++;
673
674
  iov[iovcnt].iov_base = (char *)"() ";
675
  iov[iovcnt].iov_len = 3;
676
  iovcnt++;
677
678
  iov[iovcnt].iov_base = (char *)file;
679
  iov[iovcnt].iov_len = strlen(file);
680
  iovcnt++;
681
682
  (void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
683
  iov[iovcnt].iov_base = numbuf;
684
  iov[iovcnt].iov_len = strlen(numbuf);
685
  iovcnt++;
686
    }
687
688
    /* Append newline. */
689
    iov[iovcnt].iov_base = (char *)"\n";
690
    iov[iovcnt].iov_len = 1;
691
    iovcnt++;
692
693
    /* Write message in a single syscall */
694
    ignore_result(writev(fd, iov, iovcnt));
695
}
696
697
bool
698
sudo_debug_needed_v1(int level)
699
{
700
    unsigned int subsys;
701
    int pri;
702
    struct sudo_debug_instance *instance;
703
    struct sudo_debug_output *output;
704
    bool result = false;
705
706
    if (sudo_debug_active_instance == -1)
707
        goto out;
708
709
    /* Extract priority and subsystem from level. */
710
    pri = SUDO_DEBUG_PRI(level);
711
    subsys = (unsigned int)SUDO_DEBUG_SUBSYS(level);
712
713
    if (sudo_debug_active_instance > sudo_debug_last_instance)
714
        goto out;
715
716
    instance = sudo_debug_instances[sudo_debug_active_instance];
717
    if (instance == NULL)
718
        goto out;
719
720
    if (subsys <= instance->max_subsystem) {
721
        SLIST_FOREACH(output, &instance->outputs, entries) {
722
            if (output->settings[subsys] >= pri) {
723
                result = true;
724
                break;
725
            }
726
        }
727
    }
728
out:
729
    return result;
730
}
731
732
void
733
sudo_debug_vprintf2_v1(const char *func, const char *file, int lineno, int level,
734
    const char *fmt, va_list ap)
735
{
736
    int buflen, pri, saved_errno = errno;
737
    unsigned int subsys;
738
    char static_buf[1024], *buf = static_buf;
739
    struct sudo_debug_instance *instance;
740
    struct sudo_debug_output *output;
741
    debug_decl_func(sudo_debug_vprintf2);
742
743
    if (sudo_debug_active_instance == -1)
744
  goto out;
745
746
    /* Extract priority and subsystem from level. */
747
    pri = SUDO_DEBUG_PRI(level);
748
    subsys = SUDO_DEBUG_SUBSYS(level);
749
750
    /* Find matching instance. */
751
    if (sudo_debug_active_instance > sudo_debug_last_instance) {
752
  sudo_warnx_nodebug("%s: invalid instance ID %d, max %d",
753
      __func__, sudo_debug_active_instance, sudo_debug_last_instance);
754
  goto out;
755
    }
756
    instance = sudo_debug_instances[sudo_debug_active_instance];
757
    if (instance == NULL) {
758
  sudo_warnx_nodebug("%s: unregistered instance index %d", __func__,
759
      sudo_debug_active_instance);
760
  goto out;
761
    }
762
763
    SLIST_FOREACH(output, &instance->outputs, entries) {
764
  /* Make sure we want debug info at this level. */
765
  if (subsys <= instance->max_subsystem && output->settings[subsys] >= pri) {
766
      va_list ap2;
767
768
      /* Operate on a copy of ap to support multiple outputs. */
769
      va_copy(ap2, ap);
770
      buflen = fmt ? vsnprintf(static_buf, sizeof(static_buf), fmt, ap2) : 0;
771
      va_end(ap2);
772
      if (buflen >= ssizeof(static_buf)) {
773
    va_list ap3;
774
775
    /* Not enough room in static buf, allocate dynamically. */
776
    va_copy(ap3, ap);
777
    buflen = vasprintf(&buf, fmt, ap3);
778
    va_end(ap3);
779
      }
780
      if (buflen != -1) {
781
    int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
782
    if (ISSET(level, SUDO_DEBUG_LINENO))
783
        sudo_debug_write2(output->fd, func, file, lineno, buf, buflen, errcode);
784
    else
785
        sudo_debug_write2(output->fd, NULL, NULL, 0, buf, buflen, errcode);
786
    if (buf != static_buf) {
787
        free(buf);
788
        buf = static_buf;
789
    }
790
      }
791
  }
792
    }
793
out:
794
    errno = saved_errno;
795
}
796
797
#ifdef NO_VARIADIC_MACROS
798
void
799
sudo_debug_printf_nvm_v1(int pri, const char *fmt, ...)
800
{
801
    va_list ap;
802
803
    va_start(ap, fmt);
804
    sudo_debug_vprintf2(NULL, NULL, 0, pri, fmt, ap);
805
    va_end(ap);
806
}
807
#endif /* NO_VARIADIC_MACROS */
808
809
void
810
sudo_debug_printf2_v1(const char *func, const char *file, int lineno, int level,
811
    const char *fmt, ...)
812
{
813
    va_list ap;
814
815
    va_start(ap, fmt);
816
    sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
817
    va_end(ap);
818
}
819
820
#define EXEC_PREFIX "exec "
821
822
void
823
sudo_debug_execve2_v1(int level, const char *path, char *const argv[], char *const envp[])
824
{
825
    int buflen, 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 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 = 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 >= ssizeof(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, 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
241
{
1022
241
    return 0;
1023
241
}
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
    int subsys)
1040
35.7M
{
1041
35.7M
}
1042
1043
void
1044
sudo_debug_exit_v1(const char *func, const char *file, int line,
1045
    int subsys)
1046
3.75M
{
1047
3.75M
}
1048
1049
void
1050
sudo_debug_exit_int_v1(const char *func, const char *file, int line,
1051
    int subsys, int ret)
1052
4.70M
{
1053
4.70M
}
1054
1055
void
1056
sudo_debug_exit_long_v1(const char *func, const char *file, int line,
1057
    int subsys, long ret)
1058
0
{
1059
0
}
1060
1061
void
1062
sudo_debug_exit_id_t_v1(const char *func, const char *file, int line,
1063
    int subsys, id_t ret)
1064
339k
{
1065
339k
}
1066
1067
void
1068
sudo_debug_exit_size_t_v1(const char *func, const char *file, int line,
1069
    int subsys, size_t ret)
1070
1.01M
{
1071
1.01M
}
1072
1073
void
1074
sudo_debug_exit_ssize_t_v1(const char *func, const char *file, int line,
1075
    int subsys, ssize_t ret)
1076
111k
{
1077
111k
}
1078
1079
void
1080
sudo_debug_exit_time_t_v1(const char *func, const char *file, int line,
1081
    int subsys, time_t ret)
1082
3.85k
{
1083
3.85k
}
1084
1085
void
1086
sudo_debug_exit_bool_v1(const char *func, const char *file, int line,
1087
    int subsys, bool ret)
1088
22.4M
{
1089
22.4M
}
1090
1091
void
1092
sudo_debug_exit_str_v1(const char *func, const char *file, int line,
1093
    int subsys, const char *ret)
1094
839k
{
1095
839k
}
1096
1097
void
1098
sudo_debug_exit_str_masked_v1(const char *func, const char *file, int line,
1099
    int subsys, const char *ret)
1100
0
{
1101
0
}
1102
1103
void
1104
sudo_debug_exit_ptr_v1(const char *func, const char *file, int line,
1105
    int subsys, const void *ret)
1106
2.37M
{
1107
2.37M
}
1108
1109
void
1110
sudo_debug_write2_v1(int fd, const char *func, const char *file, int lineno,
1111
    const char *str, int len, int errnum)
1112
0
{
1113
0
}
1114
1115
bool
1116
sudo_debug_needed_v1(int level)
1117
59.6k
{
1118
59.6k
    return false;
1119
59.6k
}
1120
1121
void
1122
sudo_debug_vprintf2_v1(const char *func, const char *file, int lineno, int level,
1123
    const char *fmt, va_list ap)
1124
0
{
1125
0
}
1126
1127
#ifdef NO_VARIADIC_MACROS
1128
void
1129
sudo_debug_printf_nvm_v1(int pri, const char *fmt, ...)
1130
{
1131
}
1132
#endif /* NO_VARIADIC_MACROS */
1133
1134
void
1135
sudo_debug_printf2_v1(const char *func, const char *file, int lineno, int level,
1136
    const char *fmt, ...)
1137
7.60M
{
1138
7.60M
}
1139
1140
void
1141
sudo_debug_execve2_v1(int level, const char *path, char *const argv[], char *const envp[])
1142
0
{
1143
0
}
1144
1145
int
1146
sudo_debug_get_active_instance_v1(void)
1147
0
{
1148
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1149
0
}
1150
1151
int
1152
sudo_debug_set_active_instance_v1(int idx)
1153
0
{
1154
0
    return SUDO_DEBUG_INSTANCE_INITIALIZER;
1155
0
}
1156
1157
void
1158
sudo_debug_update_fd_v1(int ofd, int nfd)
1159
0
{
1160
0
}
1161
1162
int
1163
sudo_debug_get_fds_v1(unsigned char **fds)
1164
0
{
1165
0
    return -1;
1166
0
}
1167
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */