Coverage Report

Created: 2025-10-10 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/sudo/lib/util/sudo_conf.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 2009-2023 Todd C. Miller <Todd.Miller@sudo.ws>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <config.h>
20
21
#include <sys/stat.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#ifdef HAVE_STDBOOL_H
25
# include <stdbool.h>
26
#else
27
# include <compat/stdbool.h>
28
#endif
29
#include <string.h>
30
#ifdef HAVE_STRINGS_H
31
# include <strings.h>
32
#endif /* HAVE_STRINGS_H */
33
#include <unistd.h>
34
#include <ctype.h>
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <limits.h>
38
#ifdef HAVE_DLOPEN
39
# include <dlfcn.h>
40
#endif
41
42
#define SUDO_ERROR_WRAP 0
43
44
#include <sudo_compat.h>
45
#include <sudo_conf.h>
46
#include <sudo_debug.h>
47
#include <sudo_fatal.h>
48
#include <sudo_gettext.h>
49
#include <sudo_plugin.h>
50
#include <sudo_util.h>
51
#include <pathnames.h>
52
53
#ifndef _PATH_SUDO_INTERCEPT
54
612
# define _PATH_SUDO_INTERCEPT NULL
55
#endif
56
#ifndef _PATH_SUDO_NOEXEC
57
612
# define _PATH_SUDO_NOEXEC NULL
58
#endif
59
60
struct sudo_conf_table {
61
    const char *name;
62
    unsigned int namelen;
63
    int (*parser)(const char *entry, const char *conf_file, unsigned int lineno);
64
};
65
66
struct sudo_conf_path_table {
67
    const char *pname;
68
    unsigned int pnamelen;
69
    bool dynamic;
70
    const char *pval;
71
};
72
73
struct sudo_conf_settings {
74
    bool updated;
75
    bool disable_coredump;
76
    bool probe_interfaces;
77
    int group_source;
78
    int max_groups;
79
};
80
81
static int parse_debug(const char *entry, const char *conf_file, unsigned int lineno);
82
static int parse_path(const char *entry, const char *conf_file, unsigned int lineno);
83
static int parse_plugin(const char *entry, const char *conf_file, unsigned int lineno);
84
static int parse_variable(const char *entry, const char *conf_file, unsigned int lineno);
85
86
static struct sudo_conf_table sudo_conf_table[] = {
87
    { "Debug", sizeof("Debug") - 1, parse_debug },
88
    { "Path", sizeof("Path") - 1, parse_path },
89
    { "Plugin", sizeof("Plugin") - 1, parse_plugin },
90
    { "Set", sizeof("Set") - 1, parse_variable },
91
    { NULL }
92
};
93
94
static int set_var_disable_coredump(const char *entry, const char *conf_file, unsigned int);
95
static int set_var_group_source(const char *entry, const char *conf_file, unsigned int);
96
static int set_var_max_groups(const char *entry, const char *conf_file, unsigned int);
97
static int set_var_probe_interfaces(const char *entry, const char *conf_file, unsigned int);
98
99
static struct sudo_conf_table sudo_conf_var_table[] = {
100
    { "disable_coredump", sizeof("disable_coredump") - 1, set_var_disable_coredump },
101
    { "group_source", sizeof("group_source") - 1, set_var_group_source },
102
    { "max_groups", sizeof("max_groups") - 1, set_var_max_groups },
103
    { "probe_interfaces", sizeof("probe_interfaces") - 1, set_var_probe_interfaces },
104
    { NULL }
105
};
106
107
/* Indexes into path_table[] below (order is important). */
108
1.90k
#define SUDO_CONF_PATH_ASKPASS    0
109
1.91k
#define SUDO_CONF_PATH_SESH   1
110
1.90k
#define SUDO_CONF_PATH_INTERCEPT  2
111
1.90k
#define SUDO_CONF_PATH_NOEXEC   3
112
1.90k
#define SUDO_CONF_PATH_PLUGIN_DIR 4
113
0
#define SUDO_CONF_PATH_DEVSEARCH  5
114
115
612
#define SUDO_CONF_PATH_INITIALIZER  {       \
116
612
    { "askpass", sizeof("askpass") - 1, false, _PATH_SUDO_ASKPASS }, \
117
612
    { "sesh", sizeof("sesh") - 1, false, _PATH_SUDO_SESH },   \
118
612
    { "intercept", sizeof("intercept") - 1, false, _PATH_SUDO_INTERCEPT }, \
119
612
    { "noexec", sizeof("noexec") - 1, false, _PATH_SUDO_NOEXEC }, \
120
612
    { "plugin_dir", sizeof("plugin_dir") - 1, false, _PATH_SUDO_PLUGIN_DIR }, \
121
612
    { "devsearch", sizeof("devsearch") - 1, false, _PATH_SUDO_DEVSEARCH }, \
122
612
    { NULL } \
123
612
}
124
125
/*
126
 * getgroups(2) on macOS is flakey with respect to non-local groups. 
127
 * Even with _DARWIN_UNLIMITED_GETGROUPS set we may not get all groups./
128
 * See bug #946 for details.
129
 */
130
#ifdef __APPLE__
131
# define GROUP_SOURCE_DEFAULT GROUP_SOURCE_DYNAMIC
132
#else
133
612
# define GROUP_SOURCE_DEFAULT GROUP_SOURCE_ADAPTIVE
134
#endif
135
136
612
#define SUDO_CONF_SETTINGS_INITIALIZER  {       \
137
612
    false,      /* updated */       \
138
612
    true,     /* disable_coredump */      \
139
612
    true,     /* probe_interfaces */      \
140
612
    GROUP_SOURCE_DEFAULT, /* group_source */      \
141
612
    -1        /* max_groups */      \
142
612
}
143
144
static struct sudo_conf_data {
145
    struct sudo_conf_settings settings;
146
    struct sudo_conf_debug_list debugging;
147
    struct plugin_info_list plugins;
148
    struct sudo_conf_path_table path_table[7];
149
} sudo_conf_data = {
150
    SUDO_CONF_SETTINGS_INITIALIZER,
151
    TAILQ_HEAD_INITIALIZER(sudo_conf_data.debugging),
152
    TAILQ_HEAD_INITIALIZER(sudo_conf_data.plugins),
153
    SUDO_CONF_PATH_INITIALIZER
154
};
155
156
/*
157
 * "Set variable_name value"
158
 */
159
static int
160
parse_variable(const char *entry, const char *conf_file, unsigned int lineno)
161
7.63k
{
162
7.63k
    struct sudo_conf_table *var;
163
7.63k
    int ret;
164
7.63k
    debug_decl(parse_variable, SUDO_DEBUG_UTIL);
165
166
21.7k
    for (var = sudo_conf_var_table; var->name != NULL; var++) {
167
21.1k
  if (strncmp(entry, var->name, var->namelen) == 0 &&
168
21.1k
      isblank((unsigned char)entry[var->namelen])) {
169
7.01k
      entry += var->namelen + 1;
170
7.01k
      while (isblank((unsigned char)*entry))
171
202
    entry++;
172
7.01k
      ret = var->parser(entry, conf_file, lineno);
173
7.01k
      sudo_debug_printf(ret ? SUDO_DEBUG_INFO : SUDO_DEBUG_ERROR,
174
7.01k
    "%s: %s:%u: Set %s %s", __func__, conf_file,
175
7.01k
    lineno, var->name, entry);
176
7.01k
      debug_return_int(ret);
177
7.01k
  }
178
21.1k
    }
179
7.63k
    sudo_debug_printf(SUDO_DEBUG_WARN, "%s: %s:%u: unknown setting %s",
180
621
  __func__, conf_file, lineno, entry);
181
621
    debug_return_int(false);
182
621
}
183
184
/*
185
 * "Path name /path/to/file"
186
 * If path is missing it will be set to the NULL pointer.
187
 */
188
static int
189
parse_path(const char *entry, const char *conf_file, unsigned int lineno)
190
3.08k
{
191
3.08k
    const char *entry_end = entry + strlen(entry);
192
3.08k
    const char *ep, *name, *path;
193
3.08k
    struct sudo_conf_path_table *cur;
194
3.08k
    size_t namelen;
195
3.08k
    debug_decl(parse_path, SUDO_DEBUG_UTIL);
196
197
    /* Parse name. */
198
3.08k
    name = sudo_strsplit(entry, entry_end, " \t", &ep);
199
3.08k
    if (name == NULL)
200
1.95k
  goto bad;
201
1.12k
    namelen = (size_t)(ep - name);
202
203
    /* Parse path (if present). */
204
1.12k
    path = sudo_strsplit(NULL, entry_end, " \t", &ep);
205
206
    /* Match supported paths, ignoring unknown paths. */
207
4.87k
    for (cur = sudo_conf_data.path_table; cur->pname != NULL; cur++) {
208
4.35k
  if (namelen == cur->pnamelen &&
209
891
      strncasecmp(name, cur->pname, cur->pnamelen) == 0) {
210
597
      char *pval = NULL;
211
597
      if (path != NULL) {
212
199
    if ((pval = strdup(path)) == NULL) {
213
0
        sudo_warnx(U_("%s: %s"), __func__,
214
0
      U_("unable to allocate memory"));
215
0
        debug_return_int(-1);
216
0
    }
217
199
      }
218
597
      if (cur->dynamic)
219
560
    free((char *)cur->pval);
220
597
      cur->pval = pval;
221
597
      cur->dynamic = true;
222
597
      sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %s:%u: Path %s %s",
223
597
    __func__, conf_file, lineno, cur->pname,
224
597
    pval ? pval : "(none)");
225
597
      debug_return_int(true);
226
597
  }
227
4.35k
    }
228
1.12k
    sudo_debug_printf(SUDO_DEBUG_WARN, "%s: %s:%u: unknown path %s",
229
524
  __func__, conf_file, lineno, entry);
230
524
    debug_return_int(false);
231
1.95k
bad:
232
1.95k
    sudo_warnx(U_("invalid Path value \"%s\" in %s, line %u"),
233
1.95k
  entry, conf_file, lineno);
234
1.95k
    debug_return_int(false);
235
1.95k
}
236
237
/*
238
 * "Debug program /path/to/log flags,..."
239
 */
240
static int
241
parse_debug(const char *entry, const char *conf_file, unsigned int lineno)
242
2.70k
{
243
2.70k
    struct sudo_conf_debug *debug_spec;
244
2.70k
    struct sudo_debug_file *debug_file = NULL;
245
2.70k
    const char *ep, *path, *progname, *flags;
246
2.70k
    const char *entry_end = entry + strlen(entry);
247
2.70k
    size_t pathlen, prognamelen;
248
2.70k
    debug_decl(parse_debug, SUDO_DEBUG_UTIL);
249
250
    /* Parse progname. */
251
2.70k
    progname = sudo_strsplit(entry, entry_end, " \t", &ep);
252
2.70k
    if (progname == NULL)
253
194
  debug_return_int(false); /* not enough fields */
254
2.51k
    prognamelen = (size_t)(ep - progname);
255
256
    /* Parse path. */
257
2.51k
    path = sudo_strsplit(NULL, entry_end, " \t", &ep);
258
2.51k
    if (path == NULL)
259
196
  debug_return_int(false); /* not enough fields */
260
2.31k
    pathlen = (size_t)(ep - path);
261
262
    /* Remainder is flags (freeform). */
263
2.31k
    flags = sudo_strsplit(NULL, entry_end, " \t", &ep);
264
2.31k
    if (flags == NULL)
265
196
  debug_return_int(false); /* not enough fields */
266
267
    /* If progname already exists, use it, else alloc a new one. */
268
36.8k
    TAILQ_FOREACH(debug_spec, &sudo_conf_data.debugging, entries) {
269
36.8k
  if (strncmp(debug_spec->progname, progname, prognamelen) == 0 &&
270
1.25k
      debug_spec->progname[prognamelen] == '\0')
271
810
      break;
272
36.8k
    }
273
2.11k
    if (debug_spec == NULL) {
274
1.30k
  debug_spec = malloc(sizeof(*debug_spec));
275
1.30k
  if (debug_spec == NULL)
276
0
      goto oom;
277
1.30k
  debug_spec->progname = strndup(progname, prognamelen);
278
1.30k
  if (debug_spec->progname == NULL) {
279
0
      free(debug_spec);
280
0
      debug_spec = NULL;
281
0
      goto oom;
282
0
  }
283
1.30k
  TAILQ_INIT(&debug_spec->debug_files);
284
1.30k
  TAILQ_INSERT_TAIL(&sudo_conf_data.debugging, debug_spec, entries);
285
1.30k
    }
286
2.11k
    debug_file = calloc(1, sizeof(*debug_file));
287
2.11k
    if (debug_file == NULL)
288
0
  goto oom;
289
2.11k
    debug_file->debug_file = strndup(path, pathlen);
290
2.11k
    if (debug_file->debug_file == NULL)
291
0
  goto oom;
292
2.11k
    debug_file->debug_flags = strdup(flags);
293
2.11k
    if (debug_file->debug_flags == NULL)
294
0
  goto oom;
295
2.11k
    TAILQ_INSERT_TAIL(&debug_spec->debug_files, debug_file, entries);
296
297
2.11k
    debug_return_int(true);
298
0
oom:
299
0
    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
300
0
    if (debug_file != NULL) {
301
0
  free(debug_file->debug_file);
302
0
  free(debug_file->debug_flags);
303
0
  free(debug_file);
304
0
    }
305
0
    debug_return_int(-1);
306
0
}
307
308
/*
309
 * "Plugin symbol /path/to/log args..."
310
 */
311
static int
312
parse_plugin(const char *entry, const char *conf_file, unsigned int lineno)
313
1.58k
{
314
1.58k
    struct plugin_info *info = NULL;
315
1.58k
    const char *ep, *path, *symbol;
316
1.58k
    const char *entry_end = entry + strlen(entry);
317
1.58k
    char **options = NULL;
318
1.58k
    size_t pathlen, symlen;
319
1.58k
    unsigned int nopts = 0;
320
1.58k
    debug_decl(parse_plugin, SUDO_DEBUG_UTIL);
321
322
    /* Parse symbol. */
323
1.58k
    symbol = sudo_strsplit(entry, entry_end, " \t", &ep);
324
1.58k
    if (symbol == NULL)
325
195
  debug_return_int(false); /* not enough fields */
326
1.39k
    symlen = (size_t)(ep - symbol);
327
328
    /* Parse path. */
329
1.39k
    path = sudo_strsplit(NULL, entry_end, " \t", &ep);
330
1.39k
    if (path == NULL)
331
194
  debug_return_int(false); /* not enough fields */
332
1.19k
    pathlen = (size_t)(ep - path);
333
334
    /* Split options into an array if present. */
335
1.19k
    while (isblank((unsigned char)*ep))
336
746
  ep++;
337
1.19k
    if (*ep != '\0') {
338
  /* Count number of options and allocate array. */
339
499
  const char *cp, *opt = ep;
340
341
  /* Count and allocate options array. */
342
499
  for (nopts = 0, cp = sudo_strsplit(opt, entry_end, " \t", &ep);
343
1.91k
      cp != NULL; cp = sudo_strsplit(NULL, entry_end, " \t", &ep)) {
344
1.41k
      nopts++;
345
1.41k
  }
346
499
  options = reallocarray(NULL, nopts + 1, sizeof(*options));
347
499
  if (options == NULL)
348
0
      goto oom;
349
350
  /* Fill in options array. */
351
499
  for (nopts = 0, cp = sudo_strsplit(opt, entry_end, " \t", &ep);
352
1.91k
      cp != NULL; cp = sudo_strsplit(NULL, entry_end, " \t", &ep)) {
353
1.41k
      options[nopts] = strndup(cp, (size_t)(ep - cp));
354
1.41k
      if (options[nopts] == NULL)
355
0
    goto oom;
356
1.41k
      nopts++;
357
1.41k
  }
358
499
  options[nopts] = NULL;
359
499
    }
360
361
1.19k
    info = calloc(1, sizeof(*info));
362
1.19k
    if (info == NULL)
363
0
      goto oom;
364
1.19k
    info->symbol_name = strndup(symbol, symlen);
365
1.19k
    if (info->symbol_name == NULL)
366
0
      goto oom;
367
1.19k
    info->path = strndup(path, pathlen);
368
1.19k
    if (info->path == NULL)
369
0
      goto oom;
370
1.19k
    info->options = options;
371
1.19k
    info->lineno = lineno;
372
1.19k
    TAILQ_INSERT_TAIL(&sudo_conf_data.plugins, info, entries);
373
374
1.19k
    debug_return_int(true);
375
0
oom:
376
0
    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
377
0
    if (options != NULL) {
378
0
  while (nopts)
379
0
      free(options[--nopts]);
380
0
  free(options);
381
0
    }
382
0
    if (info != NULL) {
383
0
  free(info->symbol_name);
384
0
  free(info->path);
385
0
  free(info);
386
0
    }
387
0
    debug_return_int(-1);
388
0
}
389
390
static int
391
set_var_disable_coredump(const char *strval, const char *conf_file,
392
    unsigned int lineno)
393
1.92k
{
394
1.92k
    int val = sudo_strtobool(strval);
395
1.92k
    debug_decl(set_var_disable_coredump, SUDO_DEBUG_UTIL);
396
397
1.92k
    if (val == -1) {
398
1.49k
  sudo_warnx(U_("invalid value for %s \"%s\" in %s, line %u"),
399
1.49k
      "disable_coredump", strval, conf_file, lineno);
400
1.49k
  debug_return_int(false);
401
1.49k
    }
402
421
    sudo_conf_data.settings.disable_coredump = val;
403
421
    debug_return_int(true);
404
421
}
405
406
static int
407
set_var_group_source(const char *strval, const char *conf_file,
408
    unsigned int lineno)
409
1.01k
{
410
1.01k
    debug_decl(set_var_group_source, SUDO_DEBUG_UTIL);
411
412
1.01k
    if (strcasecmp(strval, "adaptive") == 0) {
413
236
  sudo_conf_data.settings.group_source = GROUP_SOURCE_ADAPTIVE;
414
781
    } else if (strcasecmp(strval, "static") == 0) {
415
194
  sudo_conf_data.settings.group_source = GROUP_SOURCE_STATIC;
416
587
    } else if (strcasecmp(strval, "dynamic") == 0) {
417
194
  sudo_conf_data.settings.group_source = GROUP_SOURCE_DYNAMIC;
418
393
    } else {
419
393
  sudo_warnx(U_("unsupported group source \"%s\" in %s, line %u"), strval,
420
393
      conf_file, lineno);
421
393
  debug_return_int(false);
422
393
    }
423
624
    debug_return_int(true);
424
624
}
425
426
static int
427
set_var_max_groups(const char *strval, const char *conf_file,
428
    unsigned int lineno)
429
1.64k
{
430
1.64k
    int max_groups;
431
1.64k
    debug_decl(set_var_max_groups, SUDO_DEBUG_UTIL);
432
433
1.64k
    max_groups = (int)sudo_strtonum(strval, 1, 1024, NULL);
434
1.64k
    if (max_groups <= 0) {
435
1.22k
  sudo_warnx(U_("invalid max groups \"%s\" in %s, line %u"), strval,
436
1.22k
      conf_file, lineno);
437
1.22k
  debug_return_int(false);
438
1.22k
    }
439
418
    sudo_conf_data.settings.max_groups = max_groups;
440
418
    debug_return_int(true);
441
418
}
442
443
static int
444
set_var_probe_interfaces(const char *strval, const char *conf_file,
445
    unsigned int lineno)
446
2.43k
{
447
2.43k
    int val = sudo_strtobool(strval);
448
2.43k
    debug_decl(set_var_probe_interfaces, SUDO_DEBUG_UTIL);
449
450
2.43k
    if (val == -1) {
451
1.14k
  sudo_warnx(U_("invalid value for %s \"%s\" in %s, line %u"),
452
1.14k
      "probe_interfaces", strval, conf_file, lineno);
453
1.14k
  debug_return_int(false);
454
1.14k
    }
455
1.29k
    sudo_conf_data.settings.probe_interfaces = val;
456
1.29k
    debug_return_int(true);
457
1.29k
}
458
459
const char *
460
sudo_conf_askpass_path_v1(void)
461
1.90k
{
462
1.90k
    return sudo_conf_data.path_table[SUDO_CONF_PATH_ASKPASS].pval;
463
1.90k
}
464
465
const char *
466
sudo_conf_sesh_path_v1(void)
467
1.91k
{
468
1.91k
    return sudo_conf_data.path_table[SUDO_CONF_PATH_SESH].pval;
469
1.91k
}
470
471
const char *
472
sudo_conf_intercept_path_v1(void)
473
1.90k
{
474
1.90k
    return sudo_conf_data.path_table[SUDO_CONF_PATH_INTERCEPT].pval;
475
1.90k
}
476
477
const char *
478
sudo_conf_noexec_path_v1(void)
479
1.90k
{
480
1.90k
    return sudo_conf_data.path_table[SUDO_CONF_PATH_NOEXEC].pval;
481
1.90k
}
482
483
const char *
484
sudo_conf_plugin_dir_path_v1(void)
485
1.90k
{
486
1.90k
    return sudo_conf_data.path_table[SUDO_CONF_PATH_PLUGIN_DIR].pval;
487
1.90k
}
488
489
const char *
490
sudo_conf_devsearch_path_v1(void)
491
0
{
492
0
    return sudo_conf_data.path_table[SUDO_CONF_PATH_DEVSEARCH].pval;
493
0
}
494
495
int
496
sudo_conf_group_source_v1(void)
497
1.91k
{
498
1.91k
    return sudo_conf_data.settings.group_source;
499
1.91k
}
500
501
int
502
sudo_conf_max_groups_v1(void)
503
1.90k
{
504
1.90k
    return sudo_conf_data.settings.max_groups;
505
1.90k
}
506
507
struct plugin_info_list *
508
sudo_conf_plugins_v1(void)
509
3.80k
{
510
3.80k
    return &sudo_conf_data.plugins;
511
3.80k
}
512
513
struct sudo_conf_debug_list *
514
sudo_conf_debugging_v1(void)
515
3.80k
{
516
3.80k
    return &sudo_conf_data.debugging;
517
3.80k
}
518
519
/* Return the debug files list for a program, or NULL if none. */
520
struct sudo_conf_debug_file_list *
521
sudo_conf_debug_files_v1(const char *progname)
522
1.90k
{
523
1.90k
    struct sudo_conf_debug *debug_spec;
524
1.90k
    const char *progbase;
525
1.90k
    debug_decl(sudo_conf_debug_files, SUDO_DEBUG_UTIL);
526
527
    /* Determine basename if program is fully qualified (like for plugins). */
528
1.90k
    progbase = progname[0] == '/' ? sudo_basename(progname) : progname;
529
530
    /* Convert sudoedit -> sudo. */
531
1.90k
    if (strcmp(progbase, "sudoedit") == 0)
532
0
  progbase = "sudo";
533
534
1.90k
    TAILQ_FOREACH(debug_spec, &sudo_conf_data.debugging, entries) {
535
1.30k
  const char *prog = progbase;
536
537
1.30k
  if (debug_spec->progname[0] == '/') {
538
      /* Match fully-qualified name, if possible. */
539
223
      prog = progname;
540
223
  }
541
1.30k
  if (strcmp(debug_spec->progname, prog) == 0)
542
20
      debug_return_ptr(&debug_spec->debug_files);
543
544
#ifdef RTLD_MEMBER
545
  /* Handle names like sudoers.a(sudoers.so) for AIX. */
546
  const char *cp = strchr(prog, '(');
547
  const char *ep = strchr(prog, ')');
548
  if (cp != NULL && ep != NULL) {
549
      /* Match on the program name without the member. */
550
      size_t len = (size_t)(cp - prog);
551
      if (strncmp(debug_spec->progname, prog, len) == 0 &&
552
        debug_spec->progname[len] == '\0') {
553
    debug_return_ptr(&debug_spec->debug_files);
554
      }
555
556
      /* Match on the member itself. */
557
      cp++;
558
      len = (size_t)(ep - cp);
559
      if (strncmp(debug_spec->progname, cp, len) == 0 &&
560
        debug_spec->progname[len] == '\0') {
561
    debug_return_ptr(&debug_spec->debug_files);
562
      }
563
  }
564
#endif
565
1.30k
    }
566
1.88k
    debug_return_ptr(NULL);
567
1.88k
}
568
569
bool
570
sudo_conf_developer_mode_v1(void)
571
0
{
572
    /* Developer mode was removed in sudo 1.9.13. */
573
0
    return false;
574
0
}
575
576
bool
577
sudo_conf_disable_coredump_v1(void)
578
1.90k
{
579
1.90k
    return sudo_conf_data.settings.disable_coredump;
580
1.90k
}
581
582
bool
583
sudo_conf_probe_interfaces_v1(void)
584
1.90k
{
585
1.90k
    return sudo_conf_data.settings.probe_interfaces;
586
1.90k
}
587
588
/*
589
 * Free dynamically allocated parts of sudo_conf_data and
590
 * reset to initial values.
591
 */
592
static void
593
sudo_conf_init(int conf_types)
594
612
{
595
612
    struct sudo_conf_debug *debug_spec;
596
612
    struct sudo_debug_file *debug_file;
597
612
    struct plugin_info *plugin_info;
598
612
    size_t i;
599
612
    debug_decl(sudo_conf_init, SUDO_DEBUG_UTIL);
600
601
    /* Free and reset paths. */
602
612
    if (ISSET(conf_types, SUDO_CONF_PATHS)) {
603
612
  const struct sudo_conf_path_table path_table[] = SUDO_CONF_PATH_INITIALIZER;
604
612
  sudo_conf_clear_paths();
605
612
  memcpy(sudo_conf_data.path_table, path_table,
606
612
      sizeof(sudo_conf_data.path_table));
607
612
    }
608
609
    /* Free and reset debug settings. */
610
612
    if (ISSET(conf_types, SUDO_CONF_DEBUG)) {
611
1.92k
  while ((debug_spec = TAILQ_FIRST(&sudo_conf_data.debugging))) {
612
1.30k
      TAILQ_REMOVE(&sudo_conf_data.debugging, debug_spec, entries);
613
1.30k
      free(debug_spec->progname);
614
3.42k
      while ((debug_file = TAILQ_FIRST(&debug_spec->debug_files))) {
615
2.11k
    TAILQ_REMOVE(&debug_spec->debug_files, debug_file, entries);
616
2.11k
    free(debug_file->debug_file);
617
2.11k
    free(debug_file->debug_flags);
618
2.11k
    free(debug_file);
619
2.11k
      }
620
1.30k
      free(debug_spec);
621
1.30k
  }
622
612
    }
623
624
    /* Free and reset plugins. */
625
612
    if (ISSET(conf_types, SUDO_CONF_PLUGINS)) {
626
1.81k
  while ((plugin_info = TAILQ_FIRST(&sudo_conf_data.plugins))) {
627
1.19k
      TAILQ_REMOVE(&sudo_conf_data.plugins, plugin_info, entries);
628
1.19k
      free(plugin_info->symbol_name);
629
1.19k
      free(plugin_info->path);
630
1.19k
      if (plugin_info->options != NULL) {
631
1.91k
    for (i = 0; plugin_info->options[i] != NULL; i++)
632
1.41k
        free(plugin_info->options[i]);
633
499
    free(plugin_info->options);
634
499
      }
635
1.19k
      free(plugin_info);
636
1.19k
  }
637
612
    }
638
639
    /* Set initial values. */
640
612
    if (ISSET(conf_types, SUDO_CONF_SETTINGS)) {
641
612
  const struct sudo_conf_settings settings = SUDO_CONF_SETTINGS_INITIALIZER;
642
612
  sudo_conf_data.settings = settings;
643
612
    }
644
645
612
    debug_return;
646
612
}
647
648
/*
649
 * Read in /etc/sudo.conf and populates sudo_conf_data.
650
 */
651
int
652
sudo_conf_read_v1(const char *path, int conf_types)
653
1.90k
{
654
1.90k
    FILE *fp = NULL;
655
1.90k
    int fd = -1, ret = false;
656
1.90k
    char *prev_locale, *line = NULL;
657
1.90k
    unsigned int conf_lineno = 0;
658
1.90k
    char conf_file[PATH_MAX];
659
1.90k
    size_t linesize = 0;
660
1.90k
    debug_decl(sudo_conf_read, SUDO_DEBUG_UTIL);
661
662
1.90k
    if ((prev_locale = setlocale(LC_ALL, NULL)) == NULL) {
663
0
  sudo_warn("setlocale(LC_ALL, NULL)");
664
0
  debug_return_int(-1);
665
0
    }
666
1.90k
    if ((prev_locale = strdup(prev_locale)) == NULL) {
667
0
  sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
668
0
  debug_return_int(-1);
669
0
    }
670
671
    /* Parse sudo.conf in the "C" locale. */
672
1.90k
    if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
673
0
        setlocale(LC_ALL, "C");
674
675
1.90k
    if (path != NULL) {
676
  /* Caller specified a single file, which must exist. */
677
1.90k
  if (strlcpy(conf_file, path, sizeof(conf_file)) >= sizeof(conf_file)) {
678
0
      errno = ENAMETOOLONG;
679
0
      sudo_warn("%s", path);
680
0
      goto done;
681
0
  }
682
1.90k
  fd = open(conf_file, O_RDONLY);
683
1.90k
  if (fd == -1) {
684
0
      sudo_warn(U_("unable to open %s"), conf_file);
685
0
      goto done;
686
0
  }
687
1.90k
    } else {
688
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
689
  struct stat sb;
690
  int error;
691
692
  /* _PATH_SUDO_CONF is a colon-separated list of path. */
693
  fd = sudo_open_conf_path(_PATH_SUDO_CONF, conf_file,
694
      sizeof(conf_file), NULL);
695
  error = sudo_secure_fd(fd, S_IFREG, ROOT_UID, (gid_t)-1, &sb);
696
  switch (error) {
697
  case SUDO_PATH_SECURE:
698
      /* OK! */
699
      break;
700
  case SUDO_PATH_MISSING:
701
      /* Root should always be able to read sudo.conf. */
702
      if (errno != ENOENT && geteuid() == ROOT_UID)
703
    sudo_warn(U_("unable to open %s"), conf_file);
704
      goto done;
705
  case SUDO_PATH_BAD_TYPE:
706
      sudo_warnx(U_("%s is not a regular file"), conf_file);
707
      goto done;
708
  case SUDO_PATH_WRONG_OWNER:
709
      sudo_warnx(U_("%s is owned by uid %u, should be %u"),
710
    conf_file, (unsigned int) sb.st_uid, ROOT_UID);
711
      goto done;
712
  case SUDO_PATH_WORLD_WRITABLE:
713
      sudo_warnx(U_("%s is world writable"), conf_file);
714
      goto done;
715
  case SUDO_PATH_GROUP_WRITABLE:
716
      sudo_warnx(U_("%s is group writable"), conf_file);
717
      goto done;
718
  default:
719
      sudo_warnx("%s: internal error, unexpected error %d",
720
    __func__, error);
721
      goto done;
722
  }
723
#else
724
  /* No default sudo.conf when fuzzing. */
725
0
  goto done;
726
0
#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
727
0
    }
728
729
1.90k
    if ((fp = fdopen(fd, "r")) == NULL) {
730
0
  sudo_warn(U_("unable to open %s"), conf_file);
731
0
  goto done;
732
0
    }
733
734
    /* Reset to initial values if necessary. */
735
1.90k
    if (sudo_conf_data.settings.updated)
736
612
  sudo_conf_init(conf_types);
737
738
19.0k
    while (sudo_parseln(&line, &linesize, &conf_lineno, fp, 0) != -1) {
739
17.1k
  struct sudo_conf_table *cur;
740
17.1k
  size_t i;
741
17.1k
  char *cp;
742
743
17.1k
  if (*(cp = line) == '\0')
744
875
      continue;    /* empty line or comment */
745
746
50.4k
  for (i = 0, cur = sudo_conf_table; cur->name != NULL; i++, cur++) {
747
49.1k
      if (strncasecmp(cp, cur->name, cur->namelen) == 0 &&
748
49.1k
    isblank((unsigned char)cp[cur->namelen])) {
749
15.0k
    if (ISSET(conf_types, (1 << i))) {
750
15.0k
        cp += cur->namelen;
751
15.0k
        while (isblank((unsigned char)*cp))
752
15.2k
      cp++;
753
15.0k
        ret = cur->parser(cp, conf_file, conf_lineno);
754
15.0k
        switch (ret) {
755
6.66k
        case true:
756
6.66k
      sudo_conf_data.settings.updated = true;
757
6.66k
      break;
758
8.33k
        case false:
759
8.33k
      break;
760
0
        default:
761
0
      goto done;
762
15.0k
        }
763
15.0k
    }
764
15.0k
    break;
765
15.0k
      }
766
49.1k
  }
767
16.2k
  if (cur->name == NULL) {
768
1.24k
      sudo_debug_printf(SUDO_DEBUG_WARN,
769
1.24k
    "%s: %s:%u: unsupported entry: %s", __func__, conf_file,
770
1.24k
    conf_lineno, line);
771
1.24k
  }
772
16.2k
    }
773
1.90k
    ret = true;
774
775
1.90k
done:
776
1.90k
    if (fp != NULL)
777
1.90k
  fclose(fp);
778
0
    else if (fd != -1)
779
0
  close(fd);
780
1.90k
    free(line);
781
782
    /* Restore locale if needed. */
783
1.90k
    if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
784
0
        setlocale(LC_ALL, prev_locale);
785
1.90k
    free(prev_locale);
786
1.90k
    debug_return_int(ret);
787
1.90k
}
788
789
/*
790
 * Used by the sudo_conf regress test to clear compile-time path settings.
791
 */
792
void
793
sudo_conf_clear_paths_v1(void)
794
2.51k
{
795
2.51k
    struct sudo_conf_path_table *cur;
796
2.51k
    debug_decl(sudo_conf_clear_paths, SUDO_DEBUG_UTIL);
797
798
17.6k
    for (cur = sudo_conf_data.path_table; cur->pname != NULL; cur++) {
799
15.0k
  if (cur->dynamic)
800
37
      free((char *)cur->pval);
801
15.0k
  cur->pval = NULL;
802
  cur->dynamic = false;
803
15.0k
    }
804
2.51k
}