/src/sudo/plugins/sudoers/defaults.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 1999-2005, 2007-2023 |
5 | | * Todd C. Miller <Todd.Miller@sudo.ws> |
6 | | * |
7 | | * Permission to use, copy, modify, and distribute this software for any |
8 | | * purpose with or without fee is hereby granted, provided that the above |
9 | | * copyright notice and this permission notice appear in all copies. |
10 | | * |
11 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | | * |
19 | | * Sponsored in part by the Defense Advanced Research Projects |
20 | | * Agency (DARPA) and Air Force Research Laboratory, Air Force |
21 | | * Materiel Command, USAF, under agreement number F39502-99-1-0512. |
22 | | */ |
23 | | |
24 | | /* |
25 | | * This is an open source non-commercial project. Dear PVS-Studio, please check it. |
26 | | * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
27 | | */ |
28 | | |
29 | | #include <config.h> |
30 | | |
31 | | #include <sys/stat.h> |
32 | | #include <stdio.h> |
33 | | #include <stdlib.h> |
34 | | #include <string.h> |
35 | | #include <unistd.h> |
36 | | #include <ctype.h> |
37 | | #include <errno.h> |
38 | | #include <limits.h> |
39 | | #include <syslog.h> |
40 | | |
41 | | #include <sudoers.h> |
42 | | #include <sudo_eventlog.h> |
43 | | #include <sudo_iolog.h> |
44 | | #include <gram.h> |
45 | | |
46 | | static struct early_default early_defaults[] = { |
47 | | { I_IGNORE_UNKNOWN_DEFAULTS }, |
48 | | #ifdef FQDN |
49 | | { I_FQDN, true }, |
50 | | #else |
51 | | { I_FQDN }, |
52 | | #endif |
53 | | { I_MATCH_GROUP_BY_GID }, |
54 | | { I_GROUP_PLUGIN }, |
55 | | { I_RUNAS_DEFAULT }, |
56 | | { I_SUDOERS_LOCALE }, |
57 | | { -1 } |
58 | | }; |
59 | | |
60 | | /* |
61 | | * Local prototypes. |
62 | | */ |
63 | | static bool store_int(const char *str, struct sudo_defs_types *def); |
64 | | static bool store_list(const char *str, struct sudo_defs_types *def, int op); |
65 | | static bool store_mode(const char *str, struct sudo_defs_types *def); |
66 | | static int store_str(const char *str, struct sudo_defs_types *def); |
67 | | static bool store_syslogfac(const char *str, struct sudo_defs_types *def); |
68 | | static bool store_syslogpri(const char *str, struct sudo_defs_types *def); |
69 | | static bool store_timeout(const char *str, struct sudo_defs_types *def); |
70 | | static bool store_tuple(const char *str, struct sudo_defs_types *def, int op); |
71 | | static bool store_uint(const char *str, struct sudo_defs_types *def); |
72 | | static bool store_timespec(const char *str, struct sudo_defs_types *def); |
73 | | static bool store_rlimit(const char *str, struct sudo_defs_types *def); |
74 | | static bool store_plugin(const char *str, struct sudo_defs_types *def, int op); |
75 | | static bool list_op(const char *str, size_t, struct list_members *list, enum list_ops op); |
76 | | static bool valid_path(const struct sudoers_context *ctx, const struct sudo_defs_types *def, const char *val, const char *file, int line, int column, bool quiet); |
77 | | |
78 | | /* |
79 | | * Table describing compile-time and run-time options. |
80 | | */ |
81 | | #include <def_data.c> |
82 | | |
83 | | /* |
84 | | * Print version and configure info. |
85 | | */ |
86 | | void |
87 | | dump_defaults(void) |
88 | 0 | { |
89 | 0 | const struct sudo_defs_types *cur; |
90 | 0 | const struct list_member *item; |
91 | 0 | const struct def_values *def; |
92 | 0 | const char *desc; |
93 | 0 | debug_decl(dump_defaults, SUDOERS_DEBUG_DEFAULTS); |
94 | |
|
95 | 0 | for (cur = sudo_defs_table; cur->name; cur++) { |
96 | 0 | if (cur->desc) { |
97 | 0 | desc = _(cur->desc); |
98 | 0 | switch (cur->type & T_MASK) { |
99 | 0 | case T_FLAG: |
100 | 0 | if (cur->sd_un.flag) |
101 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "%s\n", desc); |
102 | 0 | break; |
103 | 0 | case T_STR: |
104 | 0 | case T_RLIMIT: |
105 | 0 | if (cur->sd_un.str) { |
106 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.str); |
107 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
108 | 0 | } |
109 | 0 | break; |
110 | 0 | case T_LOGFAC: |
111 | 0 | if (cur->sd_un.ival) { |
112 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, |
113 | 0 | sudo_logfac2str(cur->sd_un.ival)); |
114 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
115 | 0 | } |
116 | 0 | break; |
117 | 0 | case T_LOGPRI: |
118 | 0 | if (cur->sd_un.ival) { |
119 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, |
120 | 0 | sudo_logpri2str(cur->sd_un.ival)); |
121 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
122 | 0 | } |
123 | 0 | break; |
124 | 0 | case T_INT: |
125 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.ival); |
126 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
127 | 0 | break; |
128 | 0 | case T_UINT: |
129 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.uival); |
130 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
131 | 0 | break; |
132 | 0 | case T_TIMESPEC: { |
133 | | /* display timespec in minutes and 10ths of a minute */ |
134 | 0 | const int min = cur->sd_un.tspec.tv_sec / 60; |
135 | 0 | int decimin = |
136 | 0 | (((cur->sd_un.tspec.tv_sec % 60) * 10) + 30) / 60; |
137 | 0 | decimin += cur->sd_un.tspec.tv_nsec / 100000000; |
138 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, min, decimin); |
139 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
140 | 0 | break; |
141 | 0 | } |
142 | 0 | case T_MODE: |
143 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.mode); |
144 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
145 | 0 | break; |
146 | 0 | case T_LIST: |
147 | 0 | if (!SLIST_EMPTY(&cur->sd_un.list)) { |
148 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "%s\n", desc); |
149 | 0 | SLIST_FOREACH(item, &cur->sd_un.list, entries) { |
150 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, |
151 | 0 | "\t%s\n", item->value); |
152 | 0 | } |
153 | 0 | } |
154 | 0 | break; |
155 | 0 | case T_TIMEOUT: |
156 | 0 | if (cur->sd_un.ival) { |
157 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, |
158 | 0 | cur->sd_un.ival); |
159 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
160 | 0 | } |
161 | 0 | break; |
162 | 0 | case T_TUPLE: |
163 | 0 | for (def = cur->values; def->sval; def++) { |
164 | 0 | if (cur->sd_un.tuple == def->nval) { |
165 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, desc, def->sval); |
166 | 0 | break; |
167 | 0 | } |
168 | 0 | } |
169 | 0 | sudo_printf(SUDO_CONV_INFO_MSG, "\n"); |
170 | 0 | break; |
171 | 0 | } |
172 | 0 | } |
173 | 0 | } |
174 | 0 | debug_return; |
175 | 0 | } |
176 | | |
177 | | static bool |
178 | | defaults_warnx(const struct sudoers_context *ctx, const char *file, int line, |
179 | | int column, bool quiet, const char * restrict fmt, ...) |
180 | 0 | { |
181 | 0 | va_list ap; |
182 | 0 | bool ret; |
183 | 0 | debug_decl(defaults_warnx, SUDOERS_DEBUG_DEFAULTS); |
184 | |
|
185 | 0 | va_start(ap, fmt); |
186 | 0 | ret = parser_vwarnx(ctx, file, line, column, true, quiet, fmt, ap); |
187 | 0 | va_end(ap); |
188 | |
|
189 | 0 | debug_return_bool(ret); |
190 | 0 | } |
191 | | |
192 | | /* |
193 | | * Find the index of the specified Defaults name in sudo_defs_table[] |
194 | | * On success, returns the matching index or -1 on failure. |
195 | | */ |
196 | | static int |
197 | | find_default(const struct sudoers_context *ctx, const char *name, |
198 | | const char *file, int line, int column, bool quiet) |
199 | 0 | { |
200 | 0 | int i; |
201 | 0 | debug_decl(find_default, SUDOERS_DEBUG_DEFAULTS); |
202 | |
|
203 | 0 | for (i = 0; sudo_defs_table[i].name != NULL; i++) { |
204 | 0 | if (strcmp(name, sudo_defs_table[i].name) == 0) |
205 | 0 | debug_return_int(i); |
206 | 0 | } |
207 | 0 | if (!def_ignore_unknown_defaults) { |
208 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
209 | 0 | N_("unknown defaults entry \"%s\""), name); |
210 | 0 | } |
211 | 0 | debug_return_int(-1); |
212 | 0 | } |
213 | | |
214 | | /* |
215 | | * Parse a defaults entry, storing the parsed entry in sd_un. |
216 | | * Returns true on success or false on failure. |
217 | | */ |
218 | | static bool |
219 | | parse_default_entry(const struct sudoers_context *ctx, |
220 | | struct sudo_defs_types *def, const char *val, int op, |
221 | | const char *file, int line, int column, bool quiet) |
222 | 0 | { |
223 | 0 | int rc; |
224 | 0 | debug_decl(parse_default_entry, SUDOERS_DEBUG_DEFAULTS); |
225 | |
|
226 | 0 | if (file == NULL) |
227 | 0 | file = "front-end"; |
228 | |
|
229 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %s:%d:%d: %s=%s op=%d", |
230 | 0 | __func__, file, line, column, def->name, val ? val : "", op); |
231 | | |
232 | | /* |
233 | | * If no value specified, the boolean flag must be set for non-flags. |
234 | | * Only flags and tuples support boolean "true". |
235 | | */ |
236 | 0 | if (val == NULL) { |
237 | 0 | switch (def->type & T_MASK) { |
238 | 0 | case T_LOGFAC: |
239 | 0 | if (op == true) { |
240 | | /* Use default syslog facility if none specified. */ |
241 | 0 | val = LOGFAC; |
242 | 0 | } |
243 | 0 | break; |
244 | 0 | case T_FLAG: |
245 | 0 | break; |
246 | 0 | case T_TUPLE: |
247 | 0 | if (ISSET(def->type, T_BOOL)) |
248 | 0 | break; |
249 | 0 | FALLTHROUGH; |
250 | 0 | default: |
251 | 0 | if (!ISSET(def->type, T_BOOL) || op != false) { |
252 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
253 | 0 | N_("no value specified for \"%s\""), def->name); |
254 | 0 | debug_return_bool(false); |
255 | 0 | } |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | /* Only lists support append/remove. */ |
260 | 0 | if ((op == '+' || op == '-') && (def->type & T_MASK) != T_LIST) { |
261 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
262 | 0 | N_("invalid operator \"%c=\" for \"%s\""), op, def->name); |
263 | 0 | debug_return_bool(false); |
264 | 0 | } |
265 | | |
266 | 0 | switch (def->type & T_MASK) { |
267 | 0 | case T_LOGFAC: |
268 | 0 | rc = store_syslogfac(val, def); |
269 | 0 | break; |
270 | 0 | case T_LOGPRI: |
271 | 0 | rc = store_syslogpri(val, def); |
272 | 0 | break; |
273 | 0 | case T_STR: |
274 | 0 | if (val != NULL && ISSET(def->type, T_PATH|T_CHPATH)) { |
275 | 0 | if (!valid_path(ctx, def, val, file, line, column, quiet)) { |
276 | 0 | rc = -1; |
277 | 0 | break; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | rc = store_str(val, def); |
281 | 0 | break; |
282 | 0 | case T_INT: |
283 | 0 | rc = store_int(val, def); |
284 | 0 | break; |
285 | 0 | case T_UINT: |
286 | 0 | rc = store_uint(val, def); |
287 | 0 | break; |
288 | 0 | case T_MODE: |
289 | 0 | rc = store_mode(val, def); |
290 | 0 | break; |
291 | 0 | case T_FLAG: |
292 | 0 | if (val != NULL) { |
293 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
294 | 0 | N_("option \"%s\" does not take a value"), def->name); |
295 | 0 | rc = -1; |
296 | 0 | break; |
297 | 0 | } |
298 | 0 | def->sd_un.flag = (bool)op; |
299 | 0 | rc = true; |
300 | 0 | break; |
301 | 0 | case T_LIST: |
302 | 0 | rc = store_list(val, def, op); |
303 | 0 | break; |
304 | 0 | case T_TIMEOUT: |
305 | 0 | rc = store_timeout(val, def); |
306 | 0 | break; |
307 | 0 | case T_TUPLE: |
308 | 0 | rc = store_tuple(val, def, op); |
309 | 0 | break; |
310 | 0 | case T_TIMESPEC: |
311 | 0 | rc = store_timespec(val, def); |
312 | 0 | break; |
313 | 0 | case T_PLUGIN: |
314 | 0 | rc = store_plugin(val, def, op); |
315 | 0 | break; |
316 | 0 | case T_RLIMIT: |
317 | 0 | rc = store_rlimit(val, def); |
318 | 0 | break; |
319 | 0 | default: |
320 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
321 | 0 | N_("invalid Defaults type 0x%x for option \"%s\""), |
322 | 0 | def->type, def->name); |
323 | 0 | rc = -1; |
324 | 0 | break; |
325 | 0 | } |
326 | 0 | if (rc == false) { |
327 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
328 | 0 | N_("value \"%s\" is invalid for option \"%s\""), val, def->name); |
329 | 0 | } |
330 | |
|
331 | 0 | debug_return_bool(rc == true); |
332 | 0 | } |
333 | | |
334 | | static struct early_default * |
335 | | is_early_default(const char *name) |
336 | 0 | { |
337 | 0 | struct early_default *early; |
338 | 0 | debug_decl(is_early_default, SUDOERS_DEBUG_DEFAULTS); |
339 | |
|
340 | 0 | for (early = early_defaults; early->idx != -1; early++) { |
341 | 0 | if (strcmp(name, sudo_defs_table[early->idx].name) == 0) |
342 | 0 | debug_return_ptr(early); |
343 | 0 | } |
344 | 0 | debug_return_ptr(NULL); |
345 | 0 | } |
346 | | |
347 | | static bool |
348 | | run_callback(struct sudoers_context *ctx, const char *file, int line, |
349 | | int column, struct sudo_defs_types *def, int op) |
350 | 0 | { |
351 | 0 | debug_decl(run_callback, SUDOERS_DEBUG_DEFAULTS); |
352 | |
|
353 | 0 | if (def->callback == NULL) |
354 | 0 | debug_return_bool(true); |
355 | 0 | debug_return_bool(def->callback(ctx, file, line, column, &def->sd_un, op)); |
356 | 0 | } |
357 | | |
358 | | /* |
359 | | * Sets/clears an entry in the defaults structure. |
360 | | * Runs the callback if present on success. |
361 | | */ |
362 | | bool |
363 | | set_default(struct sudoers_context *ctx, const char *var, const char *val, |
364 | | int op, const char *file, int line, int column, bool quiet) |
365 | 0 | { |
366 | 0 | int idx; |
367 | 0 | debug_decl(set_default, SUDOERS_DEBUG_DEFAULTS); |
368 | |
|
369 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
370 | 0 | "%s: setting Defaults %s -> %s", __func__, var, val ? val : "false"); |
371 | |
|
372 | 0 | idx = find_default(ctx, var, file, line, column, quiet); |
373 | 0 | if (idx != -1) { |
374 | | /* Set parsed value in sudo_defs_table and run callback (if any). */ |
375 | 0 | struct sudo_defs_types *def = &sudo_defs_table[idx]; |
376 | 0 | if (parse_default_entry(ctx, def, val, op, file, line, column, quiet)) |
377 | 0 | debug_return_bool(run_callback(ctx, file, line, column, def, op)); |
378 | 0 | } |
379 | 0 | debug_return_bool(false); |
380 | 0 | } |
381 | | |
382 | | /* |
383 | | * Like set_default() but stores the matching default value |
384 | | * and does not run callbacks. |
385 | | */ |
386 | | static bool |
387 | | set_early_default(const struct sudoers_context *ctx, const char *var, |
388 | | const char *val, int op, const char *file, int line, int column, |
389 | | bool quiet, struct early_default *early) |
390 | 0 | { |
391 | 0 | int idx; |
392 | 0 | debug_decl(set_early_default, SUDOERS_DEBUG_DEFAULTS); |
393 | |
|
394 | 0 | idx = find_default(ctx, var, file, line, column, quiet); |
395 | 0 | if (idx != -1) { |
396 | | /* Set parsed value in sudo_defs_table but defer callback (if any). */ |
397 | 0 | struct sudo_defs_types *def = &sudo_defs_table[idx]; |
398 | 0 | if (parse_default_entry(ctx, def, val, op, file, line, column, quiet)) { |
399 | 0 | if (early->file != NULL) |
400 | 0 | sudo_rcstr_delref(early->file); |
401 | 0 | early->file = sudo_rcstr_addref(file); |
402 | 0 | early->line = line; |
403 | 0 | early->column = column; |
404 | 0 | early->run_callback = true; |
405 | 0 | debug_return_bool(true); |
406 | 0 | } |
407 | 0 | } |
408 | 0 | debug_return_bool(false); |
409 | 0 | } |
410 | | |
411 | | /* |
412 | | * Run callbacks for early defaults. |
413 | | */ |
414 | | static bool |
415 | | run_early_defaults(struct sudoers_context *ctx) |
416 | 4 | { |
417 | 4 | struct early_default *early; |
418 | 4 | bool ret = true; |
419 | 4 | debug_decl(run_early_defaults, SUDOERS_DEBUG_DEFAULTS); |
420 | | |
421 | 28 | for (early = early_defaults; early->idx != -1; early++) { |
422 | 24 | if (early->run_callback) { |
423 | 0 | if (!run_callback(ctx, early->file, early->line, early->column, |
424 | 0 | &sudo_defs_table[early->idx], true)) |
425 | 0 | ret = false; |
426 | 0 | early->run_callback = false; |
427 | 0 | } |
428 | 24 | } |
429 | 4 | debug_return_bool(ret); |
430 | 4 | } |
431 | | |
432 | | static void |
433 | | free_defs_val(int type, union sudo_defs_val *sd_un) |
434 | 978 | { |
435 | 978 | switch (type & T_MASK) { |
436 | 246 | case T_STR: |
437 | 312 | case T_RLIMIT: |
438 | 312 | free(sd_un->str); |
439 | 312 | break; |
440 | 30 | case T_LIST: |
441 | 30 | (void)list_op(NULL, 0, &sd_un->list, freeall); |
442 | 30 | break; |
443 | 978 | } |
444 | 978 | memset(sd_un, 0, sizeof(*sd_un)); |
445 | 978 | } |
446 | | |
447 | | static bool |
448 | | init_passprompt_regex(void) |
449 | 7 | { |
450 | 7 | struct list_member *lm; |
451 | 7 | debug_decl(init_passprompt_regex, SUDOERS_DEBUG_DEFAULTS); |
452 | | |
453 | | /* Add initial defaults setting. */ |
454 | 7 | lm = calloc(1, sizeof(struct list_member)); |
455 | 7 | if (lm == NULL || (lm->value = strdup(PASSPROMPT_REGEX)) == NULL) { |
456 | 0 | free(lm); |
457 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
458 | 0 | debug_return_bool(false); |
459 | 0 | } |
460 | 7 | SLIST_INSERT_HEAD(&def_passprompt_regex, lm, entries); |
461 | | |
462 | 7 | debug_return_bool(true); |
463 | 7 | } |
464 | | |
465 | | /* |
466 | | * Set default options to compiled-in values. |
467 | | * Any of these may be overridden at runtime by a "Defaults" file. |
468 | | */ |
469 | | bool |
470 | | init_defaults(void) |
471 | 7 | { |
472 | 7 | static bool firsttime = true; |
473 | 7 | struct sudo_defs_types *def; |
474 | 7 | debug_decl(init_defaults, SUDOERS_DEBUG_DEFAULTS); |
475 | | |
476 | | /* Clear any old settings. */ |
477 | 7 | if (!firsttime) { |
478 | 984 | for (def = sudo_defs_table; def->name != NULL; def++) |
479 | 978 | free_defs_val(def->type, &def->sd_un); |
480 | 6 | } |
481 | | |
482 | | /* First initialize the flags. */ |
483 | | #ifdef LONG_OTP_PROMPT |
484 | | def_long_otp_prompt = true; |
485 | | #endif |
486 | 7 | #ifndef ALLOW_DOT_PATH |
487 | 7 | def_ignore_dot = true; |
488 | 7 | #endif |
489 | | #ifdef ALWAYS_SEND_MAIL |
490 | | def_mail_always = true; |
491 | | #endif |
492 | 7 | #ifdef SEND_MAIL_WHEN_NO_USER |
493 | 7 | def_mail_no_user = true; |
494 | 7 | #endif |
495 | | #ifdef SEND_MAIL_WHEN_NO_HOST |
496 | | def_mail_no_host = true; |
497 | | #endif |
498 | | #ifdef SEND_MAIL_WHEN_NOT_OK |
499 | | def_mail_no_perms = true; |
500 | | #endif |
501 | 7 | #ifndef NO_LECTURE |
502 | 7 | def_lecture = once; |
503 | 7 | #endif |
504 | 7 | #ifndef NO_AUTHENTICATION |
505 | 7 | def_authenticate = true; |
506 | 7 | #endif |
507 | 7 | #ifndef NO_ROOT_SUDO |
508 | 7 | def_root_sudo = true; |
509 | 7 | #endif |
510 | | #ifdef HOST_IN_LOG |
511 | | def_log_host = true; |
512 | | #endif |
513 | | #ifdef SHELL_IF_NO_ARGS |
514 | | def_shell_noargs = true; |
515 | | #endif |
516 | | #ifdef SHELL_SETS_HOME |
517 | | def_set_home = true; |
518 | | #endif |
519 | 7 | #ifndef DONT_LEAK_PATH_INFO |
520 | 7 | def_path_info = true; |
521 | 7 | #endif |
522 | | #ifdef USE_INSULTS |
523 | | def_insults = true; |
524 | | #endif |
525 | | #ifdef FQDN |
526 | | def_fqdn = true; |
527 | | #endif |
528 | 7 | #ifdef ENV_EDITOR |
529 | 7 | def_env_editor = true; |
530 | 7 | #endif |
531 | | #ifdef UMASK_OVERRIDE |
532 | | def_umask_override = true; |
533 | | #endif |
534 | 7 | #ifdef SUDOERS_NAME_MATCH |
535 | 7 | def_fast_glob = true; |
536 | 7 | def_fdexec = never; |
537 | | #else |
538 | | def_fdexec = digest_only; |
539 | | #endif |
540 | 7 | def_timestamp_type = TIMESTAMP_TYPE; |
541 | 7 | if ((def_iolog_file = strdup(IOLOG_FILE)) == NULL) |
542 | 0 | goto oom; |
543 | 7 | if ((def_iolog_dir = strdup(_PATH_SUDO_IO_LOGDIR)) == NULL) |
544 | 0 | goto oom; |
545 | 7 | if ((def_sudoers_locale = strdup("C")) == NULL) |
546 | 0 | goto oom; |
547 | 7 | def_env_reset = ENV_RESET; |
548 | 7 | def_set_logname = true; |
549 | 7 | def_closefrom = STDERR_FILENO + 1; |
550 | 7 | def_pam_ruser = true; |
551 | | #ifdef __sun__ |
552 | | def_pam_rhost = true; |
553 | | #endif |
554 | 7 | if ((def_pam_service = strdup("sudo")) == NULL) |
555 | 0 | goto oom; |
556 | | #ifdef HAVE_PAM_LOGIN |
557 | | if ((def_pam_login_service = strdup("sudo-i")) == NULL) |
558 | | goto oom; |
559 | | #else |
560 | 7 | if ((def_pam_login_service = strdup("sudo")) == NULL) |
561 | 0 | goto oom; |
562 | 7 | #endif |
563 | | #ifdef NO_PAM_SESSION |
564 | | def_pam_session = false; |
565 | | #else |
566 | 7 | def_pam_session = true; |
567 | 7 | #endif |
568 | | #ifdef HAVE_SELINUX |
569 | | def_selinux = true; |
570 | | #endif |
571 | | #ifdef _PATH_SUDO_ADMIN_FLAG |
572 | | if ((def_admin_flag = strdup(_PATH_SUDO_ADMIN_FLAG)) == NULL) |
573 | | goto oom; |
574 | | #endif |
575 | 7 | if ((def_rlimit_core = strdup("0,0")) == NULL) |
576 | 0 | goto oom; |
577 | 7 | def_intercept_type = dso; |
578 | 7 | def_intercept_verify = true; |
579 | 7 | def_use_netgroups = true; |
580 | 7 | def_netgroup_tuple = false; |
581 | 7 | def_sudoedit_checkdir = true; |
582 | 7 | def_iolog_mode = S_IRUSR|S_IWUSR; |
583 | 7 | def_log_allowed = true; |
584 | 7 | def_log_denied = true; |
585 | 7 | def_log_format = sudo; |
586 | 7 | def_runas_allow_unknown_id = false; |
587 | 7 | def_noninteractive_auth = false; |
588 | 7 | def_use_pty = true; |
589 | | |
590 | | /* Syslog options need special care since they both strings and ints */ |
591 | 7 | #if (LOGGING & SLOG_SYSLOG) |
592 | 7 | (void) store_syslogfac(LOGFAC, &sudo_defs_table[I_SYSLOG]); |
593 | 7 | (void) store_syslogpri(PRI_SUCCESS, &sudo_defs_table[I_SYSLOG_GOODPRI]); |
594 | 7 | (void) store_syslogpri(PRI_FAILURE, &sudo_defs_table[I_SYSLOG_BADPRI]); |
595 | 7 | #endif |
596 | | |
597 | | /* Password flags also have a string and integer component. */ |
598 | 7 | (void) store_tuple("any", &sudo_defs_table[I_LISTPW], 0); |
599 | 7 | (void) store_tuple("all", &sudo_defs_table[I_VERIFYPW], 0); |
600 | | |
601 | | /* Then initialize the int-like things. */ |
602 | 7 | #ifdef SUDO_UMASK |
603 | 7 | def_umask = SUDO_UMASK; |
604 | | #else |
605 | | def_umask = ACCESSPERMS; |
606 | | #endif |
607 | 7 | def_loglinelen = MAXLOGFILELEN; |
608 | 7 | def_timestamp_timeout.tv_sec = TIMEOUT * 60; |
609 | 7 | def_passwd_timeout.tv_sec = PASSWORD_TIMEOUT * 60; |
610 | 7 | def_passwd_tries = TRIES_FOR_PASSWORD; |
611 | | #ifdef HAVE_ZLIB_H |
612 | | def_compress_io = true; |
613 | | #endif |
614 | 7 | def_ignore_audit_errors = true; |
615 | 7 | def_ignore_iolog_errors = false; |
616 | 7 | def_ignore_logfile_errors = true; |
617 | 7 | def_log_passwords = true; |
618 | 7 | #ifdef SUDOERS_LOG_CLIENT |
619 | 7 | def_log_server_timeout = 30; |
620 | 7 | def_log_server_verify = true; |
621 | 7 | def_log_server_keepalive = true; |
622 | 7 | #endif |
623 | | |
624 | | /* Now do the strings */ |
625 | 7 | if ((def_mailto = strdup(MAILTO)) == NULL) |
626 | 0 | goto oom; |
627 | 7 | if ((def_mailsub = strdup(N_(MAILSUBJECT))) == NULL) |
628 | 0 | goto oom; |
629 | 7 | if ((def_badpass_message = strdup(_(INCORRECT_PASSWORD))) == NULL) |
630 | 0 | goto oom; |
631 | 7 | #ifdef _PATH_SUDO_LECTURE_DIR |
632 | 7 | if ((def_lecture_status_dir = strdup(_PATH_SUDO_LECTURE_DIR)) == NULL) |
633 | 0 | goto oom; |
634 | 7 | #endif |
635 | 7 | #ifdef _PATH_SUDO_TIMEDIR |
636 | 7 | if ((def_timestampdir = strdup(_PATH_SUDO_TIMEDIR)) == NULL) |
637 | 0 | goto oom; |
638 | 7 | #endif |
639 | 7 | if ((def_passprompt = strdup(_(PASSPROMPT))) == NULL) |
640 | 0 | goto oom; |
641 | 7 | if ((def_runas_default = strdup(RUNAS_DEFAULT)) == NULL) |
642 | 0 | goto oom; |
643 | | #ifdef _PATH_SUDO_SENDMAIL |
644 | | if ((def_mailerpath = strdup(_PATH_SUDO_SENDMAIL)) == NULL) |
645 | | goto oom; |
646 | | #endif |
647 | 7 | if ((def_mailerflags = strdup("-t")) == NULL) |
648 | 0 | goto oom; |
649 | | #if (LOGGING & SLOG_FILE) |
650 | | if ((def_logfile = strdup(_PATH_SUDO_LOGFILE)) == NULL) |
651 | | goto oom; |
652 | | #endif |
653 | | #ifdef EXEMPTGROUP |
654 | | if ((def_exempt_group = strdup(EXEMPTGROUP)) == NULL) |
655 | | goto oom; |
656 | | #endif |
657 | | #ifdef SECURE_PATH |
658 | | if ((def_secure_path = strdup(SECURE_PATH)) == NULL) |
659 | | goto oom; |
660 | | #endif |
661 | 7 | if ((def_editor = strdup(EDITOR)) == NULL) |
662 | 0 | goto oom; |
663 | 7 | def_set_utmp = true; |
664 | 7 | def_pam_acct_mgmt = true; |
665 | 7 | def_pam_setcred = true; |
666 | 7 | def_pam_silent = true; |
667 | 7 | def_syslog_maxlen = MAXSYSLOGLEN; |
668 | 7 | def_case_insensitive_user = true; |
669 | 7 | def_case_insensitive_group = true; |
670 | | |
671 | | /* Reset the locale. */ |
672 | 7 | if (!firsttime) { |
673 | 6 | if (!sudoers_initlocale(NULL, def_sudoers_locale)) |
674 | 0 | goto oom; |
675 | 6 | } |
676 | | |
677 | | /* Finally do the lists (currently just environment tables). */ |
678 | 7 | if (!init_envtables()) |
679 | 0 | goto oom; |
680 | | |
681 | | /* Init eventlog config. */ |
682 | 7 | init_eventlog_config(); |
683 | | |
684 | | /* Initial iolog password prompt regex. */ |
685 | 7 | if (!init_passprompt_regex()) |
686 | 0 | debug_return_bool(false); |
687 | | |
688 | 7 | firsttime = false; |
689 | | |
690 | 7 | debug_return_bool(true); |
691 | 0 | oom: |
692 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
693 | 0 | debug_return_bool(false); |
694 | 0 | } |
695 | | |
696 | | /* |
697 | | * Check whether a defaults entry matches the specified type. |
698 | | * Returns true if it matches, else false. |
699 | | */ |
700 | | static bool |
701 | | default_type_matches(const struct defaults *d, int what) |
702 | 0 | { |
703 | 0 | debug_decl(default_type_matches, SUDOERS_DEBUG_DEFAULTS); |
704 | |
|
705 | 0 | switch (d->type) { |
706 | 0 | case DEFAULTS: |
707 | 0 | if (ISSET(what, SETDEF_GENERIC)) |
708 | 0 | debug_return_bool(true); |
709 | 0 | break; |
710 | 0 | case DEFAULTS_USER: |
711 | 0 | if (ISSET(what, SETDEF_USER)) |
712 | 0 | debug_return_bool(true); |
713 | 0 | break; |
714 | 0 | case DEFAULTS_RUNAS: |
715 | 0 | if (ISSET(what, SETDEF_RUNAS)) |
716 | 0 | debug_return_bool(true); |
717 | 0 | break; |
718 | 0 | case DEFAULTS_HOST: |
719 | 0 | if (ISSET(what, SETDEF_HOST)) |
720 | 0 | debug_return_bool(true); |
721 | 0 | break; |
722 | 0 | case DEFAULTS_CMND: |
723 | 0 | if (ISSET(what, SETDEF_CMND)) |
724 | 0 | debug_return_bool(true); |
725 | 0 | break; |
726 | 0 | } |
727 | 0 | debug_return_bool(false); |
728 | 0 | } |
729 | | |
730 | | /* |
731 | | * Check whether a defaults entry's binding matches. |
732 | | * Returns true if it matches, else false. |
733 | | */ |
734 | | static bool |
735 | | default_binding_matches(const struct sudoers_context *ctx, |
736 | | struct sudoers_parse_tree *parse_tree, const struct defaults *d, int what) |
737 | 0 | { |
738 | 0 | debug_decl(default_binding_matches, SUDOERS_DEBUG_DEFAULTS); |
739 | |
|
740 | 0 | switch (d->type) { |
741 | 0 | case DEFAULTS: |
742 | 0 | debug_return_bool(true); |
743 | 0 | case DEFAULTS_USER: |
744 | 0 | if (userlist_matches(parse_tree, ctx->user.pw, &d->binding->members) == ALLOW) |
745 | 0 | debug_return_bool(true); |
746 | 0 | break; |
747 | 0 | case DEFAULTS_RUNAS: |
748 | 0 | if (runaslist_matches(parse_tree, &d->binding->members, NULL, NULL, NULL) == ALLOW) |
749 | 0 | debug_return_bool(true); |
750 | 0 | break; |
751 | 0 | case DEFAULTS_HOST: |
752 | 0 | if (hostlist_matches(parse_tree, ctx->user.pw, &d->binding->members) == ALLOW) |
753 | 0 | debug_return_bool(true); |
754 | 0 | break; |
755 | 0 | case DEFAULTS_CMND: |
756 | 0 | if (cmndlist_matches(parse_tree, &d->binding->members, NULL, NULL) == ALLOW) |
757 | 0 | debug_return_bool(true); |
758 | 0 | break; |
759 | 0 | } |
760 | 0 | debug_return_bool(false); |
761 | 0 | } |
762 | | |
763 | | /* |
764 | | * Update the global defaults based on the given defaults list. |
765 | | * Pass in an OR'd list of which default types to update. |
766 | | */ |
767 | | bool |
768 | | update_defaults(struct sudoers_context *ctx, |
769 | | struct sudoers_parse_tree *parse_tree, |
770 | | const struct defaults_list *defs, int what, bool quiet) |
771 | 4 | { |
772 | 4 | const struct defaults *d; |
773 | 4 | bool global_defaults = false; |
774 | 4 | bool ret = true; |
775 | 4 | debug_decl(update_defaults, SUDOERS_DEBUG_DEFAULTS); |
776 | | |
777 | 4 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
778 | 4 | "what: 0x%02x", what); |
779 | | |
780 | | /* If no defaults list specified, use the global one in the parse tree. */ |
781 | 4 | if (defs == NULL) { |
782 | 4 | defs = &parse_tree->defaults; |
783 | 4 | global_defaults = true; |
784 | 4 | } |
785 | | |
786 | | /* |
787 | | * If using the global defaults list, apply Defaults values marked as early. |
788 | | */ |
789 | 4 | if (global_defaults) { |
790 | 4 | TAILQ_FOREACH(d, defs, entries) { |
791 | 0 | struct early_default *early = is_early_default(d->var); |
792 | 0 | if (early == NULL) |
793 | 0 | continue; |
794 | | |
795 | | /* Defaults type and binding must match. */ |
796 | 0 | if (!default_type_matches(d, what) || |
797 | 0 | !default_binding_matches(ctx, parse_tree, d, what)) |
798 | 0 | continue; |
799 | | |
800 | | /* Copy the value to sudo_defs_table and mark as early. */ |
801 | 0 | if (!set_early_default(ctx, d->var, d->val, d->op, d->file, d->line, |
802 | 0 | d->column, quiet, early)) |
803 | 0 | ret = false; |
804 | 0 | } |
805 | | |
806 | | /* Run callbacks for early defaults (if any) */ |
807 | 4 | if (!run_early_defaults(ctx)) |
808 | 0 | ret = false; |
809 | 4 | } |
810 | | |
811 | | /* |
812 | | * Set the rest of the defaults and run their callbacks, if any. |
813 | | */ |
814 | 4 | TAILQ_FOREACH(d, defs, entries) { |
815 | 0 | if (global_defaults) { |
816 | | /* Skip Defaults marked as early, we already did them. */ |
817 | 0 | if (is_early_default(d->var)) |
818 | 0 | continue; |
819 | 0 | } |
820 | | |
821 | | /* Defaults type and binding must match. */ |
822 | 0 | if (!default_type_matches(d, what) || |
823 | 0 | !default_binding_matches(ctx, parse_tree, d, what)) |
824 | 0 | continue; |
825 | | |
826 | | /* Copy the value to sudo_defs_table and run callback (if any) */ |
827 | 0 | if (!set_default(ctx, d->var, d->val, d->op, d->file, d->line, d->column, quiet)) |
828 | 0 | ret = false; |
829 | 0 | } |
830 | | |
831 | 4 | debug_return_bool(ret); |
832 | 4 | } |
833 | | |
834 | | /* |
835 | | * Check all defaults entries without actually setting them. |
836 | | */ |
837 | | bool |
838 | | check_defaults(const struct sudoers_parse_tree *parse_tree, bool quiet) |
839 | 1 | { |
840 | 1 | const struct defaults *d; |
841 | 1 | bool ret = true; |
842 | 1 | int idx; |
843 | 1 | debug_decl(check_defaults, SUDOERS_DEBUG_DEFAULTS); |
844 | | |
845 | 1 | TAILQ_FOREACH(d, &parse_tree->defaults, entries) { |
846 | 0 | idx = find_default(parse_tree->ctx, d->var, d->file, d->line, |
847 | 0 | d->column, quiet); |
848 | 0 | if (idx != -1) { |
849 | 0 | struct sudo_defs_types def = sudo_defs_table[idx]; |
850 | 0 | memset(&def.sd_un, 0, sizeof(def.sd_un)); |
851 | 0 | if (parse_default_entry(parse_tree->ctx, &def, d->val, d->op, |
852 | 0 | d->file, d->line, d->column, quiet)) { |
853 | 0 | free_defs_val(def.type, &def.sd_un); |
854 | 0 | continue; |
855 | 0 | } |
856 | 0 | } |
857 | | /* There was an error in the entry. */ |
858 | 0 | ret = false; |
859 | 0 | } |
860 | 1 | debug_return_bool(ret); |
861 | 1 | } |
862 | | |
863 | | static bool |
864 | | store_int(const char *str, struct sudo_defs_types *def) |
865 | 0 | { |
866 | 0 | const char *errstr; |
867 | 0 | int i; |
868 | 0 | debug_decl(store_int, SUDOERS_DEBUG_DEFAULTS); |
869 | |
|
870 | 0 | if (str == NULL) { |
871 | 0 | def->sd_un.ival = 0; |
872 | 0 | } else { |
873 | 0 | i = (int)sudo_strtonum(str, INT_MIN, INT_MAX, &errstr); |
874 | 0 | if (errstr != NULL) { |
875 | 0 | sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
876 | 0 | "%s: %s", str, errstr); |
877 | 0 | debug_return_bool(false); |
878 | 0 | } |
879 | 0 | def->sd_un.ival = i; |
880 | 0 | } |
881 | 0 | debug_return_bool(true); |
882 | 0 | } |
883 | | |
884 | | static bool |
885 | | store_uint(const char *str, struct sudo_defs_types *def) |
886 | 0 | { |
887 | 0 | const char *errstr; |
888 | 0 | unsigned int u; |
889 | 0 | debug_decl(store_uint, SUDOERS_DEBUG_DEFAULTS); |
890 | |
|
891 | 0 | if (str == NULL) { |
892 | 0 | def->sd_un.uival = 0; |
893 | 0 | } else { |
894 | 0 | u = (unsigned int)sudo_strtonum(str, 0, UINT_MAX, &errstr); |
895 | 0 | if (errstr != NULL) { |
896 | 0 | sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
897 | 0 | "%s: %s", str, errstr); |
898 | 0 | debug_return_bool(false); |
899 | 0 | } |
900 | 0 | def->sd_un.uival = u; |
901 | 0 | } |
902 | 0 | debug_return_bool(true); |
903 | 0 | } |
904 | | |
905 | | /* Check resource limit syntax, does not save as rlim_t. */ |
906 | | static bool |
907 | | check_rlimit(const char *str, bool soft) |
908 | 0 | { |
909 | 0 | const size_t inflen = sizeof("infinity") - 1; |
910 | 0 | debug_decl(check_rlimit, SUDOERS_DEBUG_DEFAULTS); |
911 | |
|
912 | 0 | if (isdigit((unsigned char)*str)) { |
913 | 0 | unsigned long long ullval; |
914 | 0 | char *ep; |
915 | |
|
916 | 0 | errno = 0; |
917 | 0 | #ifdef HAVE_STRTOULL |
918 | 0 | ullval = strtoull(str, &ep, 10); |
919 | 0 | if (str == ep || (errno == ERANGE && ullval == ULLONG_MAX)) |
920 | 0 | debug_return_bool(false); |
921 | | #else |
922 | | ullval = strtoul(str, &ep, 10); |
923 | | if (str == ep || (errno == ERANGE && ullval == ULONG_MAX)) |
924 | | debug_return_bool(false); |
925 | | #endif |
926 | 0 | if (*ep == '\0' || (soft && *ep == ',')) |
927 | 0 | debug_return_bool(true); |
928 | 0 | debug_return_bool(false); |
929 | 0 | } |
930 | 0 | if (strncmp(str, "infinity", inflen) == 0) { |
931 | 0 | if (str[inflen] == '\0' || (soft && str[inflen] == ',')) |
932 | 0 | debug_return_bool(true); |
933 | 0 | } |
934 | 0 | debug_return_bool(false); |
935 | 0 | } |
936 | | |
937 | | static bool |
938 | | store_rlimit(const char *str, struct sudo_defs_types *def) |
939 | 0 | { |
940 | 0 | debug_decl(store_rlimit, SUDOERS_DEBUG_DEFAULTS); |
941 | | |
942 | | /* The special values "user" and "default" are not compound. */ |
943 | 0 | if (str != NULL && strcmp(str, "user") != 0 && strcmp(str, "default") != 0) { |
944 | 0 | const char *hard, *soft = str; |
945 | | /* |
946 | | * Expect a limit in the form "soft,hard" or "limit" (both soft+hard). |
947 | | */ |
948 | 0 | hard = strchr(str, ','); |
949 | 0 | if (hard != NULL) |
950 | 0 | hard++; |
951 | 0 | else |
952 | 0 | hard = soft; |
953 | |
|
954 | 0 | if (!check_rlimit(soft, true)) |
955 | 0 | debug_return_bool(false); |
956 | 0 | if (!check_rlimit(hard, false)) |
957 | 0 | debug_return_bool(false); |
958 | 0 | } |
959 | | |
960 | | /* Store as string, front-end will parse it as a limit. */ |
961 | 0 | debug_return_bool(store_str(str, def)); |
962 | 0 | } |
963 | | |
964 | | static bool |
965 | | store_timespec(const char *str, struct sudo_defs_types *def) |
966 | 0 | { |
967 | 0 | struct timespec ts; |
968 | 0 | char sign = '+'; |
969 | 0 | long i; |
970 | 0 | debug_decl(store_timespec, SUDOERS_DEBUG_DEFAULTS); |
971 | |
|
972 | 0 | sudo_timespecclear(&ts); |
973 | 0 | if (str != NULL) { |
974 | | /* Convert from minutes to seconds. */ |
975 | 0 | if (*str == '+' || *str == '-') |
976 | 0 | sign = *str++; |
977 | 0 | while (*str != '\0' && *str != '.') { |
978 | 0 | if (!isdigit((unsigned char)*str)) |
979 | 0 | debug_return_bool(false); /* invalid number */ |
980 | | |
981 | | /* Verify (ts.tv_sec * 10) + (digit * 60) <= TIME_T_MAX. */ |
982 | 0 | i = (*str++ - '0') * 60L; |
983 | 0 | if (ts.tv_sec > (TIME_T_MAX - i) / 10) |
984 | 0 | debug_return_bool(false); /* overflow */ |
985 | 0 | ts.tv_sec *= 10; |
986 | 0 | ts.tv_sec += i; |
987 | 0 | } |
988 | 0 | if (*str++ == '.') { |
989 | 0 | long long nsec = 0; |
990 | | |
991 | | /* Convert optional fractional component to seconds and nanosecs. */ |
992 | 0 | for (i = 100000000; i > 0; i /= 10) { |
993 | 0 | if (*str == '\0') |
994 | 0 | break; |
995 | 0 | if (!isdigit((unsigned char)*str)) |
996 | 0 | debug_return_bool(false); /* invalid number */ |
997 | 0 | nsec += i * (*str++ - '0') * 60LL; |
998 | 0 | } |
999 | 0 | while (nsec >= 1000000000) { |
1000 | 0 | if (ts.tv_sec == TIME_T_MAX) |
1001 | 0 | debug_return_bool(false); /* overflow */ |
1002 | 0 | ts.tv_sec++; |
1003 | 0 | nsec -= 1000000000; |
1004 | 0 | } |
1005 | 0 | ts.tv_nsec = (long)nsec; |
1006 | 0 | } |
1007 | 0 | } |
1008 | 0 | if (sign == '-') { |
1009 | 0 | def->sd_un.tspec.tv_sec = -ts.tv_sec; |
1010 | 0 | def->sd_un.tspec.tv_nsec = -ts.tv_nsec; |
1011 | 0 | } else { |
1012 | 0 | def->sd_un.tspec.tv_sec = ts.tv_sec; |
1013 | 0 | def->sd_un.tspec.tv_nsec = ts.tv_nsec; |
1014 | 0 | } |
1015 | 0 | debug_return_bool(true); |
1016 | 0 | } |
1017 | | |
1018 | | static bool |
1019 | | store_tuple(const char *str, struct sudo_defs_types *def, int op) |
1020 | 14 | { |
1021 | 14 | const struct def_values *v; |
1022 | 14 | debug_decl(store_tuple, SUDOERS_DEBUG_DEFAULTS); |
1023 | | |
1024 | | /* |
1025 | | * Look up tuple value by name to find enum def_tuple value. |
1026 | | * A tuple must have at least two possible values. |
1027 | | */ |
1028 | 14 | if (str == NULL) { |
1029 | | /* |
1030 | | * Boolean context: true maps to values[1], false maps to values[0]. |
1031 | | */ |
1032 | 0 | if (op == true) { |
1033 | 0 | v = &def->values[1]; |
1034 | 0 | def->sd_un.ival = v->nval; |
1035 | 0 | } else if (op == false) { |
1036 | 0 | v = &def->values[0]; |
1037 | 0 | def->sd_un.ival = v->nval; |
1038 | 0 | } else { |
1039 | 0 | debug_return_bool(false); |
1040 | 0 | } |
1041 | 14 | } else { |
1042 | 28 | for (v = def->values; v->sval != NULL; v++) { |
1043 | 28 | if (strcmp(v->sval, str) == 0) { |
1044 | 14 | def->sd_un.tuple = v->nval; |
1045 | 14 | break; |
1046 | 14 | } |
1047 | 28 | } |
1048 | 14 | if (v->sval == NULL) |
1049 | 0 | debug_return_bool(false); |
1050 | 14 | } |
1051 | 14 | debug_return_bool(true); |
1052 | 14 | } |
1053 | | |
1054 | | static int |
1055 | | store_str(const char *str, struct sudo_defs_types *def) |
1056 | 0 | { |
1057 | 0 | debug_decl(store_str, SUDOERS_DEBUG_DEFAULTS); |
1058 | |
|
1059 | 0 | free(def->sd_un.str); |
1060 | 0 | if (str == NULL) { |
1061 | 0 | def->sd_un.str = NULL; |
1062 | 0 | } else { |
1063 | 0 | if ((def->sd_un.str = strdup(str)) == NULL) { |
1064 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1065 | 0 | debug_return_int(-1); |
1066 | 0 | } |
1067 | 0 | } |
1068 | 0 | debug_return_int(true); |
1069 | 0 | } |
1070 | | |
1071 | | static bool |
1072 | | store_list(const char *str, struct sudo_defs_types *def, int op) |
1073 | 0 | { |
1074 | 0 | debug_decl(store_list, SUDOERS_DEBUG_DEFAULTS); |
1075 | | |
1076 | | /* Remove all old members. */ |
1077 | 0 | if (op == false || op == true) |
1078 | 0 | (void)list_op(NULL, 0, &def->sd_un.list, freeall); |
1079 | | |
1080 | | /* Split str into multiple space-separated words and act on each one. */ |
1081 | 0 | if (str != NULL) { |
1082 | 0 | const char *cp, *ep; |
1083 | 0 | const char *end = str + strlen(str); |
1084 | 0 | const enum list_ops lop = op == '-' ? delete : add; |
1085 | |
|
1086 | 0 | if (ISSET(def->type, T_SPACE)) { |
1087 | 0 | if (!list_op(str, strlen(str), &def->sd_un.list, lop)) |
1088 | 0 | debug_return_bool(false); |
1089 | 0 | } else { |
1090 | 0 | for (cp = sudo_strsplit(str, end, " \t", &ep); cp != NULL; |
1091 | 0 | cp = sudo_strsplit(NULL, end, " \t", &ep)) { |
1092 | 0 | if (!list_op(cp, (size_t)(ep - cp), &def->sd_un.list, lop)) |
1093 | 0 | debug_return_bool(false); |
1094 | 0 | } |
1095 | 0 | } |
1096 | 0 | } |
1097 | 0 | debug_return_bool(true); |
1098 | 0 | } |
1099 | | |
1100 | | static bool |
1101 | | store_plugin(const char *str, struct sudo_defs_types *def, int op) |
1102 | 0 | { |
1103 | 0 | const enum list_ops lop = op == '-' ? delete : add; |
1104 | 0 | debug_decl(store_plugin, SUDOERS_DEBUG_DEFAULTS); |
1105 | | |
1106 | | /* Remove all old members. */ |
1107 | 0 | if (op == false || op == true) |
1108 | 0 | (void)list_op(NULL, 0, &def->sd_un.list, freeall); |
1109 | |
|
1110 | 0 | if (str != NULL) { |
1111 | 0 | if (!list_op(str, strlen(str), &def->sd_un.list, lop)) |
1112 | 0 | debug_return_bool(false); |
1113 | 0 | } |
1114 | | |
1115 | 0 | debug_return_bool(true); |
1116 | 0 | } |
1117 | | |
1118 | | static bool |
1119 | | store_syslogfac(const char *str, struct sudo_defs_types *def) |
1120 | 7 | { |
1121 | 7 | debug_decl(store_syslogfac, SUDOERS_DEBUG_DEFAULTS); |
1122 | | |
1123 | 7 | if (str == NULL) { |
1124 | 0 | def->sd_un.ival = false; |
1125 | 0 | debug_return_bool(true); |
1126 | 0 | } |
1127 | 7 | debug_return_bool(sudo_str2logfac(str, &def->sd_un.ival)); |
1128 | 7 | } |
1129 | | |
1130 | | static bool |
1131 | | store_syslogpri(const char *str, struct sudo_defs_types *def) |
1132 | 14 | { |
1133 | 14 | debug_decl(store_syslogpri, SUDOERS_DEBUG_DEFAULTS); |
1134 | | |
1135 | 14 | if (str == NULL) { |
1136 | 0 | def->sd_un.ival = -1; |
1137 | 0 | debug_return_bool(true); |
1138 | 0 | } |
1139 | 14 | debug_return_bool(sudo_str2logpri(str, &def->sd_un.ival)); |
1140 | 14 | } |
1141 | | |
1142 | | static bool |
1143 | | store_mode(const char *str, struct sudo_defs_types *def) |
1144 | 0 | { |
1145 | 0 | mode_t mode; |
1146 | 0 | const char *errstr; |
1147 | 0 | debug_decl(store_mode, SUDOERS_DEBUG_DEFAULTS); |
1148 | |
|
1149 | 0 | if (str == NULL) { |
1150 | 0 | def->sd_un.mode = ACCESSPERMS; |
1151 | 0 | } else { |
1152 | 0 | mode = sudo_strtomode(str, &errstr); |
1153 | 0 | if (errstr != NULL) { |
1154 | 0 | sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
1155 | 0 | "%s is %s", str, errstr); |
1156 | 0 | debug_return_bool(false); |
1157 | 0 | } |
1158 | 0 | def->sd_un.mode = mode; |
1159 | 0 | } |
1160 | 0 | debug_return_bool(true); |
1161 | 0 | } |
1162 | | |
1163 | | static bool |
1164 | | store_timeout(const char *str, struct sudo_defs_types *def) |
1165 | 0 | { |
1166 | 0 | debug_decl(store_mode, SUDOERS_DEBUG_DEFAULTS); |
1167 | |
|
1168 | 0 | if (str == NULL) { |
1169 | 0 | def->sd_un.ival = 0; |
1170 | 0 | } else { |
1171 | 0 | int seconds = parse_timeout(str); |
1172 | 0 | if (seconds == -1) { |
1173 | 0 | sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, |
1174 | 0 | "%s", str); |
1175 | 0 | debug_return_bool(false); |
1176 | 0 | } |
1177 | 0 | def->sd_un.ival = seconds; |
1178 | 0 | } |
1179 | 0 | debug_return_bool(true); |
1180 | 0 | } |
1181 | | |
1182 | | static bool |
1183 | | valid_path(const struct sudoers_context *ctx, const struct sudo_defs_types *def, |
1184 | | const char *val, const char *file, int line, int column, bool quiet) |
1185 | 0 | { |
1186 | 0 | bool ret = true; |
1187 | 0 | debug_decl(valid_path, SUDOERS_DEBUG_DEFAULTS); |
1188 | |
|
1189 | 0 | if (strlen(val) >= PATH_MAX) { |
1190 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
1191 | 0 | N_("path name for \"%s\" too long"), def->name); |
1192 | 0 | ret = false; |
1193 | 0 | } |
1194 | 0 | if (ISSET(def->type, T_CHPATH)) { |
1195 | 0 | if (val[0] != '/' && val[0] != '~' && (val[0] != '*' || val[1] != '\0')) { |
1196 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
1197 | 0 | N_("values for \"%s\" must start with a '/', '~', or '*'"), |
1198 | 0 | def->name); |
1199 | 0 | ret = false; |
1200 | 0 | } |
1201 | 0 | } else { |
1202 | 0 | if (val[0] != '/') { |
1203 | 0 | defaults_warnx(ctx, file, line, column, quiet, |
1204 | 0 | N_("values for \"%s\" must start with a '/'"), def->name); |
1205 | 0 | ret = false; |
1206 | 0 | } |
1207 | |
|
1208 | 0 | } |
1209 | 0 | debug_return_bool(ret); |
1210 | 0 | } |
1211 | | |
1212 | | static bool |
1213 | | list_op(const char *str, size_t len, struct list_members *list, |
1214 | | enum list_ops op) |
1215 | 30 | { |
1216 | 30 | struct list_member *cur, *prev = NULL; |
1217 | 30 | debug_decl(list_op, SUDOERS_DEBUG_DEFAULTS); |
1218 | | |
1219 | 30 | if (op == freeall) { |
1220 | 36 | while ((cur = SLIST_FIRST(list)) != NULL) { |
1221 | 6 | SLIST_REMOVE_HEAD(list, entries); |
1222 | 6 | free(cur->value); |
1223 | 6 | free(cur); |
1224 | 6 | } |
1225 | 30 | debug_return_bool(true); |
1226 | 30 | } |
1227 | | |
1228 | 0 | SLIST_FOREACH(cur, list, entries) { |
1229 | 0 | if ((strncmp(cur->value, str, len) == 0 && cur->value[len] == '\0')) { |
1230 | |
|
1231 | 0 | if (op == add) |
1232 | 0 | debug_return_bool(true); /* already exists */ |
1233 | | |
1234 | | /* Delete node */ |
1235 | 0 | if (prev == NULL) |
1236 | 0 | SLIST_REMOVE_HEAD(list, entries); |
1237 | 0 | else |
1238 | 0 | SLIST_REMOVE_AFTER(prev, entries); |
1239 | 0 | free(cur->value); |
1240 | 0 | free(cur); |
1241 | 0 | break; |
1242 | 0 | } |
1243 | 0 | prev = cur; |
1244 | 0 | } |
1245 | | |
1246 | | /* Add new node to the head of the list. */ |
1247 | 0 | if (op == add) { |
1248 | 0 | cur = calloc(1, sizeof(struct list_member)); |
1249 | 0 | if (cur == NULL || (cur->value = strndup(str, len)) == NULL) { |
1250 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1251 | 0 | free(cur); |
1252 | 0 | debug_return_bool(false); |
1253 | 0 | } |
1254 | 0 | SLIST_INSERT_HEAD(list, cur, entries); |
1255 | 0 | } |
1256 | 0 | debug_return_bool(true); |
1257 | 0 | } |
1258 | | |
1259 | | bool |
1260 | | append_default(const char *var, const char *val, int op, |
1261 | | char *source, struct defaults_list *defs) |
1262 | 0 | { |
1263 | 0 | struct defaults *def; |
1264 | 0 | debug_decl(append_default, SUDOERS_DEBUG_DEFAULTS); |
1265 | |
|
1266 | 0 | if ((def = calloc(1, sizeof(*def))) == NULL) |
1267 | 0 | goto oom; |
1268 | | |
1269 | 0 | def->type = DEFAULTS; |
1270 | 0 | def->op = op; |
1271 | 0 | if ((def->var = strdup(var)) == NULL) { |
1272 | 0 | goto oom; |
1273 | 0 | } |
1274 | 0 | if (val != NULL) { |
1275 | 0 | if ((def->val = strdup(val)) == NULL) |
1276 | 0 | goto oom; |
1277 | 0 | } |
1278 | 0 | def->file = source; |
1279 | 0 | sudo_rcstr_addref(source); |
1280 | 0 | TAILQ_INSERT_TAIL(defs, def, entries); |
1281 | 0 | debug_return_bool(true); |
1282 | | |
1283 | 0 | oom: |
1284 | 0 | if (def != NULL) { |
1285 | 0 | free(def->var); |
1286 | 0 | free(def->val); |
1287 | 0 | free(def); |
1288 | 0 | } |
1289 | 0 | debug_return_bool(false); |
1290 | 0 | } |
1291 | | |
1292 | | bool |
1293 | | cb_passprompt_regex(struct sudoers_context *ctx, const char *file, |
1294 | | int line, int column, const union sudo_defs_val *sd_un, int op) |
1295 | 0 | { |
1296 | 0 | struct list_member *lm; |
1297 | 0 | const char *errstr; |
1298 | 0 | debug_decl(cb_passprompt_regex, SUDOERS_DEBUG_DEFAULTS); |
1299 | | |
1300 | | /* If adding one or more regexps, make sure they are valid. */ |
1301 | 0 | if (op == '+' || op == true) { |
1302 | 0 | SLIST_FOREACH(lm, &sd_un->list, entries) { |
1303 | 0 | if (!sudo_regex_compile(NULL, lm->value, &errstr)) { |
1304 | 0 | defaults_warnx(ctx, file, line, column, false, |
1305 | 0 | U_("invalid regular expression \"%s\": %s"), |
1306 | 0 | lm->value, U_(errstr)); |
1307 | 0 | debug_return_bool(false); |
1308 | 0 | } |
1309 | 0 | } |
1310 | 0 | } |
1311 | | |
1312 | 0 | debug_return_bool(true); |
1313 | 0 | } |