Coverage Report

Created: 2023-06-07 06:47

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