Coverage Report

Created: 2025-07-11 06:58

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