/src/sudo/plugins/sudoers/sudoers.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 1993-1996, 1998-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 | | * Sponsored in part by the Defense Advanced Research Projects |
19 | | * Agency (DARPA) and Air Force Research Laboratory, Air Force |
20 | | * Materiel Command, USAF, under agreement number F39502-99-1-0512. |
21 | | */ |
22 | | |
23 | | /* |
24 | | * This is an open source non-commercial project. Dear PVS-Studio, please check it. |
25 | | * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
26 | | */ |
27 | | |
28 | | #ifdef __TANDEM |
29 | | # include <floss.h> |
30 | | #endif |
31 | | |
32 | | #include <config.h> |
33 | | |
34 | | #include <sys/types.h> |
35 | | #include <sys/resource.h> |
36 | | #include <sys/stat.h> |
37 | | #include <sys/socket.h> |
38 | | #include <stdio.h> |
39 | | #include <stdlib.h> |
40 | | #include <string.h> |
41 | | #include <unistd.h> |
42 | | #include <pwd.h> |
43 | | #include <errno.h> |
44 | | #include <fcntl.h> |
45 | | #include <grp.h> |
46 | | #include <netdb.h> |
47 | | #ifdef HAVE_LOGIN_CAP_H |
48 | | # include <login_cap.h> |
49 | | # ifndef LOGIN_DEFROOTCLASS |
50 | | # define LOGIN_DEFROOTCLASS "daemon" |
51 | | # endif |
52 | | # ifndef LOGIN_SETENV |
53 | | # define LOGIN_SETENV 0 |
54 | | # endif |
55 | | #endif |
56 | | #ifdef HAVE_SELINUX |
57 | | # include <selinux/selinux.h> |
58 | | #endif |
59 | | #include <ctype.h> |
60 | | #ifndef HAVE_GETADDRINFO |
61 | | # include <compat/getaddrinfo.h> |
62 | | #endif |
63 | | |
64 | | #include <sudoers.h> |
65 | | #include <timestamp.h> |
66 | | #include <sudo_iolog.h> |
67 | | |
68 | | /* |
69 | | * Prototypes |
70 | | */ |
71 | | static int set_cmnd(struct sudoers_context *ctx); |
72 | | static bool init_vars(struct sudoers_context *ctx, char * const *); |
73 | | static bool set_loginclass(struct sudoers_context *); |
74 | | static bool set_runaspw(struct sudoers_context *ctx, const char *, bool); |
75 | | static bool set_runasgr(struct sudoers_context *ctx, const char *, bool); |
76 | | |
77 | | /* |
78 | | * Globals |
79 | | */ |
80 | | static char *prev_user; |
81 | | static struct sudoers_context sudoers_ctx = SUDOERS_CONTEXT_INITIALIZER; |
82 | | static struct sudo_nss_list *snl; |
83 | | static bool unknown_runas_uid; |
84 | | static bool unknown_runas_gid; |
85 | | static int cmnd_status = NOT_FOUND_ERROR; |
86 | | static struct defaults_list initial_defaults = TAILQ_HEAD_INITIALIZER(initial_defaults); |
87 | | |
88 | | #ifdef __linux__ |
89 | | static struct rlimit nproclimit; |
90 | | #endif |
91 | | |
92 | | #ifdef SUDOERS_LOG_CLIENT |
93 | 6.61k | # define remote_iologs (!SLIST_EMPTY(&def_log_servers)) |
94 | | #else |
95 | | # define remote_iologs 0 |
96 | | #endif |
97 | | |
98 | | /* |
99 | | * Unlimit the number of processes since Linux's setuid() will |
100 | | * apply resource limits when changing uid and return EAGAIN if |
101 | | * nproc would be exceeded by the uid switch. |
102 | | */ |
103 | | static void |
104 | | unlimit_nproc(void) |
105 | 29.2k | { |
106 | 29.2k | #ifdef __linux__ |
107 | 29.2k | struct rlimit rl; |
108 | 29.2k | debug_decl(unlimit_nproc, SUDOERS_DEBUG_UTIL); |
109 | | |
110 | 29.2k | if (getrlimit(RLIMIT_NPROC, &nproclimit) != 0) |
111 | 0 | sudo_warn("getrlimit(RLIMIT_NPROC)"); |
112 | 29.2k | rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; |
113 | 29.2k | if (setrlimit(RLIMIT_NPROC, &rl) != 0) { |
114 | 0 | rl.rlim_cur = rl.rlim_max = nproclimit.rlim_max; |
115 | 0 | if (setrlimit(RLIMIT_NPROC, &rl) != 0) |
116 | 0 | sudo_warn("setrlimit(RLIMIT_NPROC)"); |
117 | 0 | } |
118 | 29.2k | debug_return; |
119 | 29.2k | #endif /* __linux__ */ |
120 | 29.2k | } |
121 | | |
122 | | /* |
123 | | * Restore saved value of RLIMIT_NPROC. |
124 | | */ |
125 | | static void |
126 | | restore_nproc(void) |
127 | 29.2k | { |
128 | 29.2k | #ifdef __linux__ |
129 | 29.2k | debug_decl(restore_nproc, SUDOERS_DEBUG_UTIL); |
130 | | |
131 | 29.2k | if (setrlimit(RLIMIT_NPROC, &nproclimit) != 0) |
132 | 0 | sudo_warn("setrlimit(RLIMIT_NPROC)"); |
133 | | |
134 | 29.2k | debug_return; |
135 | 29.2k | #endif /* __linux__ */ |
136 | 29.2k | } |
137 | | |
138 | | /* |
139 | | * Re-initialize Defaults settings. |
140 | | * We do not warn, log or send mail for errors when reinitializing, |
141 | | * this would have already been done the first time through. |
142 | | */ |
143 | | static bool |
144 | | sudoers_reinit_defaults(struct sudoers_context *ctx) |
145 | 6.39k | { |
146 | 6.39k | struct sudo_nss *nss, *nss_next; |
147 | 6.39k | sudoers_logger_t logger = sudoers_error_hook; |
148 | 6.39k | debug_decl(sudoers_reinit_defaults, SUDOERS_DEBUG_PLUGIN); |
149 | | |
150 | 6.39k | if (!init_defaults()) { |
151 | 0 | sudo_warnx("%s", U_("unable to initialize sudoers default values")); |
152 | 0 | debug_return_bool(false); |
153 | 0 | } |
154 | | |
155 | | /* It should not be possible for the initial defaults to fail to apply. */ |
156 | 6.39k | if (!update_defaults(ctx, NULL, &initial_defaults, |
157 | 6.39k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) |
158 | 0 | debug_return_bool(false); |
159 | | |
160 | | /* Disable error logging while re-processing defaults. */ |
161 | 6.39k | sudoers_error_hook = NULL; |
162 | | |
163 | 6.39k | TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { |
164 | | /* Missing/invalid defaults is not a fatal error. */ |
165 | 6.39k | if (nss->getdefs(ctx, nss) != -1) { |
166 | 6.39k | (void)update_defaults(ctx, nss->parse_tree, NULL, |
167 | 6.39k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, true); |
168 | 6.39k | } |
169 | 6.39k | } |
170 | | |
171 | | /* Restore error logging. */ |
172 | 6.39k | sudoers_error_hook = logger; |
173 | | |
174 | | /* No need to check the admin flag file multiple times. */ |
175 | 6.39k | if (ISSET(ctx->mode, MODE_POLICY_INTERCEPTED)) { |
176 | 0 | free(def_admin_flag); |
177 | 0 | def_admin_flag = NULL; |
178 | 0 | } |
179 | | |
180 | 6.39k | debug_return_bool(true); |
181 | 6.39k | } |
182 | | |
183 | | int |
184 | | sudoers_init(void *info, sudoers_logger_t logger, char * const envp[]) |
185 | 27.3k | { |
186 | 27.3k | struct sudo_nss *nss, *nss_next; |
187 | 27.3k | int oldlocale, sources = 0; |
188 | 27.3k | static int ret = -1; |
189 | 27.3k | debug_decl(sudoers_init, SUDOERS_DEBUG_PLUGIN); |
190 | | |
191 | | /* Only initialize once. */ |
192 | 27.3k | if (snl != NULL) |
193 | 0 | debug_return_int(ret); |
194 | | |
195 | 27.3k | bindtextdomain("sudoers", LOCALEDIR); |
196 | | |
197 | | /* Hook up logging function for parse errors. */ |
198 | 27.3k | sudoers_error_hook = logger; |
199 | | |
200 | | /* Register fatal/fatalx callback. */ |
201 | 27.3k | sudo_fatal_callback_register(sudoers_cleanup); |
202 | | |
203 | | /* Initialize environment functions (including replacements). */ |
204 | 27.3k | if (!env_init(envp)) |
205 | 0 | debug_return_int(-1); |
206 | | |
207 | | /* Setup defaults data structures. */ |
208 | 27.3k | if (!init_defaults()) { |
209 | 0 | sudo_warnx("%s", U_("unable to initialize sudoers default values")); |
210 | 0 | debug_return_int(-1); |
211 | 0 | } |
212 | | |
213 | | /* Parse info from front-end. */ |
214 | 27.3k | sudoers_ctx.mode = sudoers_policy_deserialize_info(&sudoers_ctx, info, |
215 | 27.3k | &initial_defaults); |
216 | 27.3k | if (ISSET(sudoers_ctx.mode, MODE_ERROR)) |
217 | 5.31k | debug_return_int(-1); |
218 | | |
219 | 21.9k | if (!init_vars(&sudoers_ctx, envp)) |
220 | 421 | debug_return_int(-1); |
221 | | |
222 | | /* Parse nsswitch.conf for sudoers order. */ |
223 | 21.5k | snl = sudo_read_nss(); |
224 | | |
225 | | /* LDAP or NSS may modify the euid so we need to be root for the open. */ |
226 | 21.5k | if (!set_perms(NULL, PERM_ROOT)) |
227 | 0 | debug_return_int(-1); |
228 | | |
229 | | /* Use the C locale unless another is specified in sudoers. */ |
230 | 21.5k | sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); |
231 | 21.5k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
232 | | |
233 | | /* Update defaults set by front-end. */ |
234 | 21.5k | if (!update_defaults(&sudoers_ctx, NULL, &initial_defaults, |
235 | 21.5k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { |
236 | 0 | goto cleanup; |
237 | 0 | } |
238 | | |
239 | | /* Open and parse sudoers, set global defaults. */ |
240 | 21.5k | TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { |
241 | 21.5k | if (nss->open(&sudoers_ctx, nss) == -1 || (nss->parse_tree = nss->parse(&sudoers_ctx, nss)) == NULL) { |
242 | 0 | TAILQ_REMOVE(snl, nss, entries); |
243 | 0 | continue; |
244 | 0 | } |
245 | 21.5k | sources++; |
246 | | |
247 | | /* Missing/invalid defaults is not a fatal error. */ |
248 | 21.5k | if (nss->getdefs(&sudoers_ctx, nss) == -1) { |
249 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR|SLOG_NO_STDERR, |
250 | 0 | N_("unable to get defaults from %s"), nss->source); |
251 | 21.5k | } else { |
252 | 21.5k | (void)update_defaults(&sudoers_ctx, nss->parse_tree, NULL, |
253 | 21.5k | SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false); |
254 | 21.5k | } |
255 | 21.5k | } |
256 | 21.5k | if (sources == 0) { |
257 | | /* Display an extra warning if there are multiple sudoers sources. */ |
258 | 0 | if (TAILQ_FIRST(snl) != TAILQ_LAST(snl, sudo_nss_list)) |
259 | 0 | sudo_warnx("%s", U_("no valid sudoers sources found, quitting")); |
260 | 0 | goto cleanup; |
261 | 0 | } |
262 | | |
263 | | /* Set login class if applicable (after sudoers is parsed). */ |
264 | 21.5k | if (set_loginclass(&sudoers_ctx)) |
265 | 21.5k | ret = true; |
266 | | |
267 | 21.5k | cleanup: |
268 | 21.5k | mail_parse_errors(&sudoers_ctx); |
269 | | |
270 | 21.5k | if (!restore_perms()) |
271 | 0 | ret = -1; |
272 | | |
273 | | /* Restore user's locale. */ |
274 | 21.5k | sudo_warn_set_locale_func(NULL); |
275 | 21.5k | sudoers_setlocale(oldlocale, NULL); |
276 | | |
277 | 21.5k | debug_return_int(ret); |
278 | 21.5k | } |
279 | | |
280 | | /* |
281 | | * Expand I/O log dir and file into a full path. |
282 | | * Returns the full I/O log path prefixed with "iolog_path=". |
283 | | * Sets ctx->iolog_file and ctx->iolog_path as a side effect. |
284 | | */ |
285 | | static char * |
286 | | format_iolog_path(struct sudoers_context *ctx) |
287 | 3.06k | { |
288 | 3.06k | char dir[PATH_MAX], file[PATH_MAX]; |
289 | 3.06k | char *iolog_path = NULL; |
290 | 3.06k | int oldlocale; |
291 | 3.06k | bool ok; |
292 | 3.06k | debug_decl(format_iolog_path, SUDOERS_DEBUG_PLUGIN); |
293 | | |
294 | | /* Use sudoers locale for strftime() */ |
295 | 3.06k | sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); |
296 | 3.06k | ok = expand_iolog_path(def_iolog_dir, dir, sizeof(dir), |
297 | 3.06k | &sudoers_iolog_path_escapes[1], ctx); |
298 | 3.06k | if (ok) { |
299 | 3.06k | ctx->iolog_dir = dir; |
300 | 3.06k | ok = expand_iolog_path(def_iolog_file, file, sizeof(file), |
301 | 3.06k | &sudoers_iolog_path_escapes[0], ctx); |
302 | 3.06k | ctx->iolog_dir = NULL; |
303 | 3.06k | } |
304 | 3.06k | sudoers_setlocale(oldlocale, NULL); |
305 | 3.06k | if (!ok) |
306 | 0 | goto done; |
307 | | |
308 | 3.06k | if (asprintf(&iolog_path, "iolog_path=%s/%s", dir, file) == -1) { |
309 | 0 | iolog_path = NULL; |
310 | 0 | goto done; |
311 | 0 | } |
312 | | |
313 | | /* Stash pointer to the I/O log for the event log. */ |
314 | 3.06k | ctx->iolog_path = iolog_path + sizeof("iolog_path=") - 1; |
315 | 3.06k | ctx->iolog_file = ctx->iolog_path + 1 + strlen(dir); |
316 | | |
317 | 3.06k | done: |
318 | 3.06k | debug_return_str(iolog_path); |
319 | 3.06k | } |
320 | | |
321 | | static void |
322 | | cb_lookup(const struct sudoers_parse_tree *parse_tree, |
323 | | const struct userspec *us, int user_match, const struct privilege *priv, |
324 | | int host_match, const struct cmndspec *cs, int date_match, int runas_match, |
325 | | int cmnd_match, void *closure) |
326 | 0 | { |
327 | 0 | struct sudoers_match_info *info = closure; |
328 | |
|
329 | 0 | if (cmnd_match != UNSPEC) { |
330 | 0 | info->us = us; |
331 | 0 | info->priv = priv; |
332 | 0 | info->cs = cs; |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | | /* |
337 | | * Find the command, perform a sudoers lookup, ask for a password as |
338 | | * needed, and perform post-lookup checks. Logs success/failure. |
339 | | * This is used by the check, list and validate plugin methods. |
340 | | * |
341 | | * Returns true if allowed, false if denied, -1 on error and |
342 | | * -2 for usage error. |
343 | | */ |
344 | | static int |
345 | | sudoers_check_common(struct sudoers_context *ctx, int pwflag) |
346 | 29.2k | { |
347 | 29.2k | struct sudoers_match_info match_info = { NULL }; |
348 | 29.2k | int oldlocale, ret = -1; |
349 | 29.2k | unsigned int validated; |
350 | 29.2k | time_t now; |
351 | 29.2k | debug_decl(sudoers_check_common, SUDOERS_DEBUG_PLUGIN); |
352 | | |
353 | | /* The user may only specify a host for "sudo -l". */ |
354 | 29.2k | if (!ISSET(ctx->mode, MODE_LIST|MODE_CHECK)) { |
355 | 18.5k | if (strcmp(ctx->runas.host, ctx->user.host) != 0) { |
356 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
357 | 0 | N_("user not allowed to set remote host for command")); |
358 | 0 | sudo_warnx("%s", |
359 | 0 | U_("a remote host may only be specified when listing privileges.")); |
360 | 0 | ret = false; |
361 | 0 | goto done; |
362 | 0 | } |
363 | 18.5k | } |
364 | | |
365 | | /* If given the -P option, set the "preserve_groups" flag. */ |
366 | 29.2k | if (ISSET(ctx->mode, MODE_PRESERVE_GROUPS)) |
367 | 71 | def_preserve_groups = true; |
368 | | |
369 | | /* Find command in path and apply per-command Defaults. */ |
370 | 29.2k | cmnd_status = set_cmnd(ctx); |
371 | 29.2k | if (cmnd_status == NOT_FOUND_ERROR) |
372 | 0 | goto done; |
373 | | |
374 | | /* Is root even allowed to run sudo? */ |
375 | 29.2k | if (ctx->user.uid == 0 && !def_root_sudo) { |
376 | | /* Not an audit event (should it be?). */ |
377 | 0 | sudo_warnx("%s", |
378 | 0 | U_("sudoers specifies that root is not allowed to sudo")); |
379 | 0 | ret = false; |
380 | 0 | goto done; |
381 | 0 | } |
382 | | |
383 | | /* Check for -C overriding def_closefrom. */ |
384 | 29.2k | if (ctx->user.closefrom >= 0 && ctx->user.closefrom != def_closefrom) { |
385 | 1.34k | if (!def_closefrom_override) { |
386 | 1.34k | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
387 | 1.34k | N_("user not allowed to override closefrom limit")); |
388 | 1.34k | sudo_warnx("%s", U_("you are not permitted to use the -C option")); |
389 | 1.34k | goto bad; |
390 | 1.34k | } |
391 | 0 | def_closefrom = ctx->user.closefrom; |
392 | 0 | } |
393 | | |
394 | | /* |
395 | | * Check sudoers sources, using the locale specified in sudoers. |
396 | | */ |
397 | 27.9k | time(&now); |
398 | 27.9k | sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); |
399 | 27.9k | validated = sudoers_lookup(snl, ctx, now, cb_lookup, &match_info, |
400 | 27.9k | &cmnd_status, pwflag); |
401 | 27.9k | sudoers_setlocale(oldlocale, NULL); |
402 | 27.9k | if (ISSET(validated, VALIDATE_ERROR)) { |
403 | | /* The lookup function should have printed an error. */ |
404 | 0 | goto done; |
405 | 0 | } |
406 | | |
407 | 27.9k | if (match_info.us != NULL && match_info.us->file != NULL) { |
408 | 0 | free(ctx->source); |
409 | 0 | if (match_info.us->line != 0) { |
410 | 0 | if (asprintf(&ctx->source, "%s:%d:%d", match_info.us->file, |
411 | 0 | match_info.us->line, match_info.us->column) == -1) |
412 | 0 | ctx->source = NULL; |
413 | 0 | } else { |
414 | 0 | ctx->source = strdup(match_info.us->file); |
415 | 0 | } |
416 | 0 | if (ctx->source == NULL) { |
417 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
418 | 0 | goto done; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | 27.9k | if (ctx->runas.cmnd == NULL) { |
423 | 27.9k | if ((ctx->runas.cmnd = strdup(ctx->user.cmnd)) == NULL) { |
424 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
425 | 0 | goto done; |
426 | 0 | } |
427 | 27.9k | } |
428 | | |
429 | | /* Defer uid/gid checks until after defaults have been updated. */ |
430 | 27.9k | if (unknown_runas_uid && !def_runas_allow_unknown_id) { |
431 | 52 | log_warningx(ctx, SLOG_AUDIT, N_("unknown user %s"), |
432 | 52 | ctx->runas.pw->pw_name); |
433 | 52 | goto done; |
434 | 52 | } |
435 | 27.8k | if (ctx->runas.gr != NULL) { |
436 | 329 | if (unknown_runas_gid && !def_runas_allow_unknown_id) { |
437 | 67 | log_warningx(ctx, SLOG_AUDIT, N_("unknown group %s"), |
438 | 67 | ctx->runas.gr->gr_name); |
439 | 67 | goto done; |
440 | 67 | } |
441 | 329 | } |
442 | | |
443 | | /* If no command line args and "shell_noargs" is not set, error out. */ |
444 | 27.8k | if (ISSET(ctx->mode, MODE_IMPLIED_SHELL) && !def_shell_noargs) { |
445 | | /* Not an audit event. */ |
446 | 35 | ret = -2; /* usage error */ |
447 | 35 | goto done; |
448 | 35 | } |
449 | | |
450 | | /* Bail if a tty is required and we don't have one. */ |
451 | 27.7k | if (def_requiretty && !sudoers_tty_present(ctx)) { |
452 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, N_("no tty")); |
453 | 0 | sudo_warnx("%s", U_("sorry, you must have a tty to run sudo")); |
454 | 0 | goto bad; |
455 | 0 | } |
456 | | |
457 | | /* Check runas user's shell if running (or checking) a command. */ |
458 | 27.7k | if (ISSET(ctx->mode, MODE_RUN|MODE_CHECK)) { |
459 | 16.1k | if (!user_shell_valid(ctx->runas.pw)) { |
460 | 0 | log_warningx(ctx, SLOG_RAW_MSG|SLOG_AUDIT, |
461 | 0 | N_("invalid shell for user %s: %s"), |
462 | 0 | ctx->runas.pw->pw_name, ctx->runas.pw->pw_shell); |
463 | 0 | goto bad; |
464 | 0 | } |
465 | 16.1k | } |
466 | | |
467 | | /* |
468 | | * We don't reset the environment for sudoedit or if the user |
469 | | * specified the -E command line flag and they have setenv privs. |
470 | | */ |
471 | 27.7k | if (ISSET(ctx->mode, MODE_EDIT) || |
472 | 27.7k | (ISSET(ctx->mode, MODE_PRESERVE_ENV) && def_setenv)) |
473 | 1.46k | def_env_reset = false; |
474 | | |
475 | | /* Build a new environment that avoids any nasty bits. */ |
476 | 27.7k | if (!rebuild_env(ctx)) |
477 | 0 | goto bad; |
478 | | |
479 | | /* Require a password if sudoers says so. */ |
480 | 27.7k | switch (check_user(ctx, validated, ctx->mode)) { |
481 | 27.7k | case AUTH_SUCCESS: |
482 | | /* user authenticated successfully. */ |
483 | 27.7k | break; |
484 | 0 | case AUTH_FAILURE: |
485 | | /* Note: log_denial() calls audit for us. */ |
486 | 0 | if (!ISSET(validated, VALIDATE_SUCCESS)) { |
487 | | /* Only display a denial message if no password was read. */ |
488 | 0 | if (!log_denial(ctx, validated, def_passwd_tries <= 0)) |
489 | 0 | goto done; |
490 | 0 | } |
491 | 0 | goto bad; |
492 | 0 | default: |
493 | | /* some other error, ret is -1. */ |
494 | 0 | goto done; |
495 | 27.7k | } |
496 | | |
497 | | /* Check whether ctx->runas.chroot is permitted (if specified). */ |
498 | 27.7k | switch (check_user_runchroot(ctx->runas.chroot)) { |
499 | 27.7k | case true: |
500 | 27.7k | break; |
501 | 0 | case false: |
502 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
503 | 0 | N_("user not allowed to change root directory to %s"), |
504 | 0 | ctx->runas.chroot); |
505 | 0 | sudo_warnx(U_("you are not permitted to use the -R option with %s"), |
506 | 0 | ctx->user.cmnd); |
507 | 0 | goto bad; |
508 | 0 | default: |
509 | 0 | goto done; |
510 | 27.7k | } |
511 | | |
512 | | /* Check whether ctx->runas.cwd is permitted (if specified). */ |
513 | 27.7k | switch (check_user_runcwd(ctx->runas.cwd)) { |
514 | 27.7k | case true: |
515 | 27.7k | break; |
516 | 0 | case false: |
517 | 0 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
518 | 0 | N_("user not allowed to change directory to %s"), ctx->runas.cwd); |
519 | 0 | sudo_warnx(U_("you are not permitted to use the -D option with %s"), |
520 | 0 | ctx->user.cmnd); |
521 | 0 | goto bad; |
522 | 0 | default: |
523 | 0 | goto done; |
524 | 27.7k | } |
525 | | |
526 | | /* If run as root with SUDO_USER set, set ctx->user.pw to that user. */ |
527 | | /* XXX - causes confusion when root is not listed in sudoers */ |
528 | 27.7k | if (ISSET(ctx->mode, MODE_RUN|MODE_EDIT) && prev_user != NULL) { |
529 | 0 | if (ctx->user.uid == 0 && strcmp(prev_user, "root") != 0) { |
530 | 0 | struct passwd *pw; |
531 | |
|
532 | 0 | if ((pw = sudo_getpwnam(prev_user)) != NULL) { |
533 | 0 | if (ctx->user.pw != NULL) |
534 | 0 | sudo_pw_delref(ctx->user.pw); |
535 | 0 | ctx->user.pw = pw; |
536 | 0 | } |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | | /* If the user was not allowed to run the command we are done. */ |
541 | 27.7k | if (!ISSET(validated, VALIDATE_SUCCESS)) { |
542 | | /* Note: log_failure() calls audit for us. */ |
543 | 0 | if (!log_failure(ctx, validated, cmnd_status)) |
544 | 0 | goto done; |
545 | 0 | goto bad; |
546 | 0 | } |
547 | | |
548 | | /* |
549 | | * Check if the user is trying to run a setid binary in intercept mode. |
550 | | * For the DSO intercept_type, we reject attempts to run setid binaries |
551 | | * by default since the dynamic loader will clear LD_PRELOAD, defeating |
552 | | * intercept. |
553 | | */ |
554 | 27.7k | if (def_intercept || ISSET(ctx->mode, MODE_POLICY_INTERCEPTED)) { |
555 | 0 | if (!def_intercept_allow_setid && ctx->user.cmnd_stat != NULL) { |
556 | 0 | if (ISSET(ctx->user.cmnd_stat->st_mode, S_ISUID|S_ISGID)) { |
557 | 0 | CLR(validated, VALIDATE_SUCCESS); |
558 | 0 | if (!log_denial(ctx, validated|FLAG_INTERCEPT_SETID, true)) |
559 | 0 | goto done; |
560 | 0 | goto bad; |
561 | 0 | } |
562 | 0 | } |
563 | 0 | } |
564 | | |
565 | | /* Create Ubuntu-style dot file to indicate sudo was successful. */ |
566 | 27.7k | if (create_admin_success_flag(ctx) == -1) |
567 | 0 | goto done; |
568 | | |
569 | | /* Finally tell the user if the command did not exist. */ |
570 | 27.7k | if (cmnd_status == NOT_FOUND_DOT) { |
571 | 3.73k | audit_failure(ctx, ctx->runas.argv, N_("command in current directory")); |
572 | 3.73k | sudo_warnx(U_("ignoring \"%s\" found in '.'\nUse \"sudo ./%s\" if this is the \"%s\" you wish to run."), ctx->user.cmnd, ctx->user.cmnd, ctx->user.cmnd); |
573 | 3.73k | goto bad; |
574 | 24.0k | } else if (cmnd_status == NOT_FOUND) { |
575 | 1.90k | if (ISSET(ctx->mode, MODE_CHECK)) { |
576 | 0 | audit_failure(ctx, ctx->runas.argv, N_("%s: command not found"), |
577 | 0 | ctx->runas.argv[1]); |
578 | 0 | sudo_warnx(U_("%s: command not found"), ctx->runas.argv[1]); |
579 | 1.90k | } else { |
580 | 1.90k | audit_failure(ctx, ctx->runas.argv, N_("%s: command not found"), |
581 | 1.90k | ctx->user.cmnd); |
582 | 1.90k | sudo_warnx(U_("%s: command not found"), ctx->user.cmnd); |
583 | 1.90k | if (strncmp(ctx->user.cmnd, "cd", 2) == 0 && (ctx->user.cmnd[2] == '\0' || |
584 | 0 | isblank((unsigned char)ctx->user.cmnd[2]))) { |
585 | 0 | sudo_warnx("%s", |
586 | 0 | U_("\"cd\" is a shell built-in command, it cannot be run directly.")); |
587 | 0 | sudo_warnx("%s", |
588 | 0 | U_("the -s option may be used to run a privileged shell.")); |
589 | 0 | sudo_warnx("%s", |
590 | 0 | U_("the -D option may be used to run a command in a specific directory.")); |
591 | 0 | } |
592 | 1.90k | } |
593 | 1.90k | goto bad; |
594 | 1.90k | } |
595 | | |
596 | | /* If user specified a timeout make sure sudoers allows it. */ |
597 | 22.1k | if (!def_user_command_timeouts && ctx->user.timeout > 0) { |
598 | 681 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
599 | 681 | N_("user not allowed to set a command timeout")); |
600 | 681 | sudo_warnx("%s", |
601 | 681 | U_("sorry, you are not allowed set a command timeout")); |
602 | 681 | goto bad; |
603 | 681 | } |
604 | | |
605 | | /* If user specified env vars make sure sudoers allows it. */ |
606 | 21.4k | if (ISSET(ctx->mode, MODE_RUN) && !def_setenv) { |
607 | 7.10k | if (ISSET(ctx->mode, MODE_PRESERVE_ENV)) { |
608 | 28 | log_warningx(ctx, SLOG_NO_STDERR|SLOG_AUDIT, |
609 | 28 | N_("user not allowed to preserve the environment")); |
610 | 28 | sudo_warnx("%s", |
611 | 28 | U_("sorry, you are not allowed to preserve the environment")); |
612 | 28 | goto bad; |
613 | 7.07k | } else { |
614 | 7.07k | if (!validate_env_vars(ctx, ctx->user.env_add)) |
615 | 1.75k | goto bad; |
616 | 7.07k | } |
617 | 7.10k | } |
618 | | |
619 | 19.6k | ret = true; |
620 | 19.6k | goto done; |
621 | | |
622 | 9.44k | bad: |
623 | 9.44k | ret = false; |
624 | 29.2k | done: |
625 | 29.2k | debug_return_int(ret); |
626 | 29.2k | } |
627 | | |
628 | | static bool need_reinit; |
629 | | |
630 | | /* |
631 | | * Check whether the user is allowed to run the specified command. |
632 | | * Returns true if allowed, false if denied, -1 on error and |
633 | | * -2 for usage error. |
634 | | */ |
635 | | int |
636 | | sudoers_check_cmnd(int argc, char * const argv[], char *env_add[], |
637 | | void *closure) |
638 | 14.9k | { |
639 | 14.9k | char *iolog_path = NULL; |
640 | 14.9k | mode_t cmnd_umask = ACCESSPERMS; |
641 | 14.9k | int ret = -1; |
642 | 14.9k | debug_decl(sudoers_check_cmnd, SUDOERS_DEBUG_PLUGIN); |
643 | | |
644 | 14.9k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
645 | | |
646 | 14.9k | if (argc == 0) { |
647 | 0 | sudo_warnx("%s", U_("no command specified")); |
648 | 0 | debug_return_int(-1); |
649 | 0 | } |
650 | | |
651 | 14.9k | if (need_reinit) { |
652 | | /* Was previous command intercepted? */ |
653 | 6.39k | if (ISSET(sudoers_ctx.mode, MODE_RUN) && def_intercept) |
654 | 0 | SET(sudoers_ctx.mode, MODE_POLICY_INTERCEPTED); |
655 | | |
656 | | /* Only certain mode flags are legal for intercepted commands. */ |
657 | 6.39k | if (ISSET(sudoers_ctx.mode, MODE_POLICY_INTERCEPTED)) |
658 | 0 | sudoers_ctx.mode &= MODE_INTERCEPT_MASK; |
659 | | |
660 | | /* Re-initialize defaults if we are called multiple times. */ |
661 | 6.39k | if (!sudoers_reinit_defaults(&sudoers_ctx)) |
662 | 0 | debug_return_int(-1); |
663 | 6.39k | } |
664 | 14.9k | need_reinit = true; |
665 | | |
666 | 14.9k | unlimit_nproc(); |
667 | | |
668 | 14.9k | if (!set_perms(&sudoers_ctx, PERM_INITIAL)) |
669 | 0 | goto bad; |
670 | | |
671 | | /* Environment variables specified on the command line. */ |
672 | 14.9k | if (env_add != NULL && env_add[0] != NULL) |
673 | 6.35k | sudoers_ctx.user.env_add = env_add; |
674 | | |
675 | | /* |
676 | | * Make a local copy of argc/argv, with special handling for the |
677 | | * '-i' option. We also allocate an extra slot for bash's --login. |
678 | | */ |
679 | 14.9k | if (sudoers_ctx.runas.argv != NULL && sudoers_ctx.runas.argv != sudoers_ctx.runas.argv_saved) { |
680 | 3.40k | sudoers_gc_remove(GC_PTR, sudoers_ctx.runas.argv); |
681 | 3.40k | free(sudoers_ctx.runas.argv); |
682 | 3.40k | } |
683 | 14.9k | sudoers_ctx.runas.argv = reallocarray(NULL, (size_t)argc + 2, sizeof(char *)); |
684 | 14.9k | if (sudoers_ctx.runas.argv == NULL) { |
685 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
686 | 0 | goto error; |
687 | 0 | } |
688 | 14.9k | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv); |
689 | 14.9k | memcpy(sudoers_ctx.runas.argv, argv, (size_t)argc * sizeof(char *)); |
690 | 14.9k | sudoers_ctx.runas.argc = argc; |
691 | 14.9k | sudoers_ctx.runas.argv[sudoers_ctx.runas.argc] = NULL; |
692 | 14.9k | if (ISSET(sudoers_ctx.mode, MODE_LOGIN_SHELL) && sudoers_ctx.runas.pw != NULL) { |
693 | 308 | sudoers_ctx.runas.argv[0] = strdup(sudoers_ctx.runas.pw->pw_shell); |
694 | 308 | if (sudoers_ctx.runas.argv[0] == NULL) { |
695 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
696 | 0 | goto error; |
697 | 0 | } |
698 | 308 | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv[0]); |
699 | 308 | } |
700 | | |
701 | 14.9k | ret = sudoers_check_common(&sudoers_ctx, 0); |
702 | 14.9k | if (ret != true) |
703 | 8.37k | goto done; |
704 | | |
705 | 6.61k | if (!remote_iologs) { |
706 | 3.06k | if (iolog_enabled && def_iolog_file && def_iolog_dir) { |
707 | 3.06k | if ((iolog_path = format_iolog_path(&sudoers_ctx)) == NULL) { |
708 | 0 | if (!def_ignore_iolog_errors) |
709 | 0 | goto error; |
710 | | /* Unable to expand I/O log path, disable I/O logging. */ |
711 | 0 | def_log_input = false; |
712 | 0 | def_log_output = false; |
713 | 0 | def_log_stdin = false; |
714 | 0 | def_log_stdout = false; |
715 | 0 | def_log_stderr = false; |
716 | 0 | def_log_ttyin = false; |
717 | 0 | def_log_ttyout = false; |
718 | 0 | } |
719 | 3.06k | } |
720 | 3.06k | } |
721 | | |
722 | | /* |
723 | | * Set umask based on sudoers. |
724 | | * If user's umask is more restrictive, OR in those bits too |
725 | | * unless umask_override is set. |
726 | | */ |
727 | 6.61k | if (def_umask != ACCESSPERMS) { |
728 | 6.61k | cmnd_umask = def_umask; |
729 | 6.61k | if (!def_umask_override) |
730 | 6.61k | cmnd_umask |= sudoers_ctx.user.umask; |
731 | 6.61k | } |
732 | | |
733 | 6.61k | if (ISSET(sudoers_ctx.mode, MODE_LOGIN_SHELL)) { |
734 | 144 | char *p; |
735 | | |
736 | | /* Convert /bin/sh -> -sh so shell knows it is a login shell */ |
737 | 144 | if ((p = strrchr(sudoers_ctx.runas.argv[0], '/')) == NULL) |
738 | 0 | p = sudoers_ctx.runas.argv[0]; |
739 | 144 | *p = '-'; |
740 | 144 | sudoers_ctx.runas.argv[0] = p; |
741 | | |
742 | | /* |
743 | | * Newer versions of bash require the --login option to be used |
744 | | * in conjunction with the -c option even if the shell name starts |
745 | | * with a '-'. Unfortunately, bash 1.x uses -login, not --login |
746 | | * so this will cause an error for that. |
747 | | */ |
748 | 144 | if (sudoers_ctx.runas.argc > 1 && strcmp(sudoers_ctx.runas.argv[0], "-bash") == 0 && |
749 | 144 | strcmp(sudoers_ctx.runas.argv[1], "-c") == 0) { |
750 | | /* We allocated extra space for the --login above. */ |
751 | 0 | memmove(&sudoers_ctx.runas.argv[2], &sudoers_ctx.runas.argv[1], |
752 | 0 | (size_t)sudoers_ctx.runas.argc * sizeof(char *)); |
753 | 0 | sudoers_ctx.runas.argv[1] = (char *)"--login"; |
754 | 0 | sudoers_ctx.runas.argc++; |
755 | 0 | } |
756 | | |
757 | 144 | #ifdef _PATH_ENVIRONMENT |
758 | | /* Insert system-wide environment variables. */ |
759 | 144 | if (!read_env_file(&sudoers_ctx, _PATH_ENVIRONMENT, true, false)) |
760 | 0 | sudo_warn("%s", _PATH_ENVIRONMENT); |
761 | 144 | #endif |
762 | | #ifdef HAVE_LOGIN_CAP_H |
763 | | /* Set environment based on login class. */ |
764 | | if (sudoers_ctx.runas.class) { |
765 | | login_cap_t *lc = login_getclass(sudoers_ctx.runas.class); |
766 | | if (lc != NULL) { |
767 | | setusercontext(lc, sudoers_ctx.runas.pw, |
768 | | sudoers_ctx.runas.pw->pw_uid, LOGIN_SETPATH|LOGIN_SETENV); |
769 | | login_close(lc); |
770 | | } |
771 | | } |
772 | | #endif /* HAVE_LOGIN_CAP_H */ |
773 | 144 | } |
774 | | |
775 | | /* Insert system-wide environment variables. */ |
776 | 6.61k | if (def_restricted_env_file) { |
777 | 6.61k | if (!read_env_file(&sudoers_ctx, def_restricted_env_file, false, true)) |
778 | 0 | sudo_warn("%s", def_restricted_env_file); |
779 | 6.61k | } |
780 | 6.61k | if (def_env_file) { |
781 | 6.61k | if (!read_env_file(&sudoers_ctx, def_env_file, false, false)) |
782 | 0 | sudo_warn("%s", def_env_file); |
783 | 6.61k | } |
784 | | |
785 | | /* Insert user-specified environment variables. */ |
786 | 6.61k | if (!insert_env_vars(sudoers_ctx.user.env_add)) { |
787 | 138 | sudo_warnx("%s", |
788 | 138 | U_("error setting user-specified environment variables")); |
789 | 138 | goto error; |
790 | 138 | } |
791 | | |
792 | | /* Note: must call audit before uid change. */ |
793 | 6.48k | if (ISSET(sudoers_ctx.mode, MODE_EDIT)) { |
794 | 1.27k | const char *env_editor = NULL; |
795 | 1.27k | char **edit_argv; |
796 | 1.27k | int edit_argc; |
797 | | |
798 | 1.27k | sudoers_ctx.sudoedit_nfiles = sudoers_ctx.runas.argc - 1; |
799 | 1.27k | free(sudoers_ctx.runas.cmnd); |
800 | 1.27k | sudoers_ctx.runas.cmnd = find_editor(sudoers_ctx.sudoedit_nfiles, |
801 | 1.27k | sudoers_ctx.runas.argv + 1, &edit_argc, &edit_argv, NULL, &env_editor); |
802 | 1.27k | if (sudoers_ctx.runas.cmnd == NULL) { |
803 | 479 | switch (errno) { |
804 | 479 | case ENOENT: |
805 | 479 | audit_failure(&sudoers_ctx, sudoers_ctx.runas.argv, |
806 | 479 | N_("%s: command not found"), |
807 | 479 | env_editor ? env_editor : def_editor); |
808 | 479 | sudo_warnx(U_("%s: command not found"), |
809 | 479 | env_editor ? env_editor : def_editor); |
810 | 479 | goto error; |
811 | 0 | case EINVAL: |
812 | 0 | if (def_env_editor && env_editor != NULL) { |
813 | | /* User tried to do something funny with the editor. */ |
814 | 0 | log_warningx(&sudoers_ctx, |
815 | 0 | SLOG_NO_STDERR|SLOG_AUDIT|SLOG_SEND_MAIL, |
816 | 0 | "invalid user-specified editor: %s", env_editor); |
817 | 0 | goto error; |
818 | 0 | } |
819 | 0 | FALLTHROUGH; |
820 | 0 | default: |
821 | 0 | goto error; |
822 | 479 | } |
823 | 479 | } |
824 | | /* find_editor() already g/c'd edit_argv[] */ |
825 | 794 | if (sudoers_ctx.runas.argv != sudoers_ctx.runas.argv_saved) { |
826 | 794 | sudoers_gc_remove(GC_PTR, sudoers_ctx.runas.argv); |
827 | 794 | free(sudoers_ctx.runas.argv); |
828 | 794 | } |
829 | 794 | sudoers_ctx.runas.argv = edit_argv; |
830 | 794 | sudoers_ctx.runas.argc = edit_argc; |
831 | | |
832 | | /* We want to run the editor with the unmodified environment. */ |
833 | 794 | env_swap_old(); |
834 | 794 | } |
835 | | |
836 | | /* Save the initial command and argv so we have it for exit logging. */ |
837 | 6.00k | if (sudoers_ctx.runas.cmnd_saved == NULL) { |
838 | 3.02k | sudoers_ctx.runas.cmnd_saved = strdup(sudoers_ctx.runas.cmnd); |
839 | 3.02k | if (sudoers_ctx.runas.cmnd_saved == NULL) { |
840 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
841 | 0 | goto error; |
842 | 0 | } |
843 | 3.02k | sudoers_ctx.runas.argv_saved = sudoers_ctx.runas.argv; |
844 | 3.02k | } |
845 | | |
846 | 6.00k | ret = true; |
847 | 6.00k | goto done; |
848 | | |
849 | 0 | bad: |
850 | 0 | ret = false; |
851 | 0 | goto done; |
852 | | |
853 | 617 | error: |
854 | 617 | ret = -1; |
855 | | |
856 | 14.9k | done: |
857 | 14.9k | mail_parse_errors(&sudoers_ctx); |
858 | | |
859 | 14.9k | if (def_group_plugin) |
860 | 0 | group_plugin_unload(); |
861 | 14.9k | reset_parser(); |
862 | | |
863 | 14.9k | if (ret == -1) { |
864 | | /* Free locally-allocated strings. */ |
865 | 680 | free(iolog_path); |
866 | 14.3k | } else { |
867 | | /* Store settings to pass back to front-end. */ |
868 | 14.3k | if (!sudoers_policy_store_result(&sudoers_ctx, ret, |
869 | 14.3k | sudoers_ctx.runas.argv, env_get(), cmnd_umask, iolog_path, closure)) |
870 | 0 | ret = -1; |
871 | 14.3k | } |
872 | | |
873 | | /* Zero out stashed copy of environment, it is owned by the front-end. */ |
874 | 14.9k | (void)env_init(NULL); |
875 | | |
876 | 14.9k | if (!rewind_perms()) |
877 | 0 | ret = -1; |
878 | | |
879 | 14.9k | restore_nproc(); |
880 | | |
881 | 14.9k | sudo_warn_set_locale_func(NULL); |
882 | | |
883 | 14.9k | debug_return_int(ret); |
884 | 14.9k | } |
885 | | |
886 | | /* |
887 | | * Validate the user and update their timestamp file entry. |
888 | | * Returns true if allowed, false if denied, -1 on error and |
889 | | * -2 for usage error. |
890 | | */ |
891 | | int |
892 | | sudoers_validate_user(void) |
893 | 3.56k | { |
894 | 3.56k | int ret = -1; |
895 | 3.56k | debug_decl(sudoers_validate_user, SUDOERS_DEBUG_PLUGIN); |
896 | | |
897 | 3.56k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
898 | | |
899 | 3.56k | unlimit_nproc(); |
900 | | |
901 | 3.56k | if (!set_perms(&sudoers_ctx, PERM_INITIAL)) |
902 | 0 | goto done; |
903 | | |
904 | 3.56k | sudoers_ctx.runas.argv = reallocarray(NULL, 2, sizeof(char *)); |
905 | 3.56k | if (sudoers_ctx.runas.argv == NULL) { |
906 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
907 | 0 | goto done; |
908 | 0 | } |
909 | 3.56k | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv); |
910 | 3.56k | sudoers_ctx.runas.argv[0] = (char *)"validate"; |
911 | 3.56k | sudoers_ctx.runas.argv[1] = NULL; |
912 | 3.56k | sudoers_ctx.runas.argc = 2; |
913 | | |
914 | 3.56k | ret = sudoers_check_common(&sudoers_ctx, I_VERIFYPW); |
915 | | |
916 | 3.56k | done: |
917 | 3.56k | mail_parse_errors(&sudoers_ctx); |
918 | | |
919 | 3.56k | if (def_group_plugin) |
920 | 0 | group_plugin_unload(); |
921 | 3.56k | reset_parser(); |
922 | 3.56k | env_init(NULL); |
923 | | |
924 | 3.56k | if (!rewind_perms()) |
925 | 0 | ret = -1; |
926 | | |
927 | 3.56k | restore_nproc(); |
928 | | |
929 | 3.56k | sudo_warn_set_locale_func(NULL); |
930 | | |
931 | 3.56k | debug_return_int(ret); |
932 | 3.56k | } |
933 | | |
934 | | /* |
935 | | * List a user's privileges or check whether a specific command may be run. |
936 | | * Returns true if allowed, false if denied, -1 on error and |
937 | | * -2 for usage error. |
938 | | */ |
939 | | int |
940 | | sudoers_list(int argc, char * const argv[], const char *list_user, int verbose) |
941 | 10.7k | { |
942 | 10.7k | struct passwd *pw; |
943 | 10.7k | int ret = -1; |
944 | 10.7k | debug_decl(sudoers_list, SUDOERS_DEBUG_PLUGIN); |
945 | | |
946 | 10.7k | sudo_warn_set_locale_func(sudoers_warn_setlocale); |
947 | | |
948 | 10.7k | unlimit_nproc(); |
949 | | |
950 | 10.7k | if (!set_perms(&sudoers_ctx, PERM_INITIAL)) |
951 | 0 | goto done; |
952 | | |
953 | 10.7k | if (list_user) { |
954 | 3.56k | if (sudoers_ctx.runas.list_pw != NULL) |
955 | 1.78k | sudo_pw_delref(sudoers_ctx.runas.list_pw); |
956 | 3.56k | sudoers_ctx.runas.list_pw = sudo_getpwnam(list_user); |
957 | 3.56k | if (sudoers_ctx.runas.list_pw == NULL) { |
958 | 0 | sudo_warnx(U_("unknown user %s"), list_user); |
959 | 0 | goto done; |
960 | 0 | } |
961 | 3.56k | } |
962 | | |
963 | 10.7k | sudoers_ctx.runas.argv = reallocarray(NULL, (size_t)argc + 2, sizeof(char *)); |
964 | 10.7k | if (sudoers_ctx.runas.argv == NULL) { |
965 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
966 | 0 | goto done; |
967 | 0 | } |
968 | 10.7k | sudoers_gc_add(GC_PTR, sudoers_ctx.runas.argv); |
969 | 10.7k | sudoers_ctx.runas.argv[0] = (char *)"list"; |
970 | 10.7k | if (argc != 0) |
971 | 3.56k | memcpy(sudoers_ctx.runas.argv + 1, argv, (size_t)argc * sizeof(char *)); |
972 | 10.7k | sudoers_ctx.runas.argc = argc + 1; |
973 | 10.7k | sudoers_ctx.runas.argv[sudoers_ctx.runas.argc] = NULL; |
974 | | |
975 | 10.7k | ret = sudoers_check_common(&sudoers_ctx, I_LISTPW); |
976 | 10.7k | if (ret != true) |
977 | 912 | goto done; |
978 | | |
979 | 9.79k | pw = sudoers_ctx.runas.list_pw ? sudoers_ctx.runas.list_pw : sudoers_ctx.user.pw; |
980 | 9.79k | if (ISSET(sudoers_ctx.mode, MODE_CHECK)) |
981 | 3.26k | ret = display_cmnd(&sudoers_ctx, snl, pw, verbose); |
982 | 6.52k | else |
983 | 6.52k | ret = display_privs(&sudoers_ctx, snl, pw, verbose); |
984 | | |
985 | 10.7k | done: |
986 | 10.7k | mail_parse_errors(&sudoers_ctx); |
987 | | |
988 | 10.7k | if (def_group_plugin) |
989 | 0 | group_plugin_unload(); |
990 | 10.7k | reset_parser(); |
991 | 10.7k | env_init(NULL); |
992 | | |
993 | 10.7k | if (!rewind_perms()) |
994 | 0 | ret = -1; |
995 | | |
996 | 10.7k | restore_nproc(); |
997 | | |
998 | 10.7k | sudo_warn_set_locale_func(NULL); |
999 | | |
1000 | 10.7k | debug_return_int(ret); |
1001 | 10.7k | } |
1002 | | |
1003 | | /* |
1004 | | * Initialize timezone and fill in ctx->user. |
1005 | | */ |
1006 | | static bool |
1007 | | init_vars(struct sudoers_context *ctx, char * const envp[]) |
1008 | 21.9k | { |
1009 | 21.9k | char * const * ep; |
1010 | 21.9k | bool unknown_user = false; |
1011 | 21.9k | debug_decl(init_vars, SUDOERS_DEBUG_PLUGIN); |
1012 | | |
1013 | 21.9k | if (!sudoers_initlocale(setlocale(LC_ALL, NULL), def_sudoers_locale)) { |
1014 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1015 | 0 | debug_return_bool(false); |
1016 | 0 | } |
1017 | | |
1018 | 21.9k | #define MATCHES(s, v) \ |
1019 | 263k | (strncmp((s), (v), sizeof(v) - 1) == 0 && (s)[sizeof(v) - 1] != '\0') |
1020 | | |
1021 | 21.9k | ctx->user.envp = envp; |
1022 | 835k | for (ep = ctx->user.envp; *ep; ep++) { |
1023 | 813k | switch (**ep) { |
1024 | 21.9k | case 'K': |
1025 | 21.9k | if (MATCHES(*ep, "KRB5CCNAME=")) |
1026 | 21.9k | ctx->user.ccname = *ep + sizeof("KRB5CCNAME=") - 1; |
1027 | 21.9k | break; |
1028 | 43.9k | case 'P': |
1029 | 43.9k | if (MATCHES(*ep, "PATH=")) |
1030 | 21.9k | ctx->user.path = *ep + sizeof("PATH=") - 1; |
1031 | 43.9k | break; |
1032 | 109k | case 'S': |
1033 | 109k | if (MATCHES(*ep, "SUDO_PROMPT=")) { |
1034 | | /* Don't override "sudo -p prompt" */ |
1035 | 21.9k | if (ctx->user.prompt == NULL) |
1036 | 21.5k | ctx->user.prompt = *ep + sizeof("SUDO_PROMPT=") - 1; |
1037 | 21.9k | break; |
1038 | 21.9k | } |
1039 | 87.9k | if (MATCHES(*ep, "SUDO_USER=")) |
1040 | 0 | prev_user = *ep + sizeof("SUDO_USER=") - 1; |
1041 | 87.9k | break; |
1042 | 813k | } |
1043 | 813k | } |
1044 | 21.9k | #undef MATCHES |
1045 | | |
1046 | 21.9k | if (ctx->user.pw == NULL) { |
1047 | | /* Fake a struct passwd for the call to log_warningx(). */ |
1048 | 258 | ctx->user.pw = sudo_mkpwent(ctx->user.name, ctx->user.uid, |
1049 | 258 | ctx->user.gid, NULL, NULL); |
1050 | 258 | unknown_user = true; |
1051 | 258 | } |
1052 | 21.9k | if (ctx->user.gid_list == NULL) |
1053 | 21.9k | ctx->user.gid_list = sudo_get_gidlist(ctx->user.pw, ENTRY_TYPE_ANY); |
1054 | | |
1055 | | /* Store initialize permissions so we can restore them later. */ |
1056 | 21.9k | if (!set_perms(ctx, PERM_INITIAL)) |
1057 | 0 | debug_return_bool(false); |
1058 | | |
1059 | | /* Set parse callbacks */ |
1060 | 21.9k | set_callbacks(); |
1061 | | |
1062 | | /* It is now safe to use log_warningx() and set_perms() */ |
1063 | 21.9k | if (unknown_user) { |
1064 | 258 | log_warningx(ctx, SLOG_SEND_MAIL, N_("unknown user %s"), ctx->user.name); |
1065 | 258 | debug_return_bool(false); |
1066 | 258 | } |
1067 | | |
1068 | | /* |
1069 | | * Set runas passwd/group entries based on command line or sudoers. |
1070 | | * Note that if runas_group was specified without runas_user we |
1071 | | * run the command as the invoking user. |
1072 | | */ |
1073 | 21.7k | if (ctx->runas.group != NULL) { |
1074 | 711 | if (!set_runasgr(ctx, ctx->runas.group, false)) |
1075 | 118 | debug_return_bool(false); |
1076 | 593 | if (!set_runaspw(ctx, ctx->runas.user ? |
1077 | 500 | ctx->runas.user : ctx->user.name, false)) |
1078 | 3 | debug_return_bool(false); |
1079 | 21.0k | } else { |
1080 | 21.0k | if (!set_runaspw(ctx, ctx->runas.user ? |
1081 | 20.7k | ctx->runas.user : def_runas_default, false)) |
1082 | 42 | debug_return_bool(false); |
1083 | 21.0k | } |
1084 | | |
1085 | 21.5k | debug_return_bool(true); |
1086 | 21.5k | } |
1087 | | |
1088 | | /* |
1089 | | * Fill in ctx->user.cmnd and ctx->user.cmnd_stat variables. |
1090 | | * Does not fill in ctx->user.cmnd_base. |
1091 | | */ |
1092 | | int |
1093 | | set_cmnd_path(struct sudoers_context *ctx, const char *runchroot) |
1094 | 23.7k | { |
1095 | 23.7k | const char *cmnd_in; |
1096 | 23.7k | char *cmnd_out = NULL; |
1097 | 23.7k | char *path = ctx->user.path; |
1098 | 23.7k | int ret; |
1099 | 23.7k | debug_decl(set_cmnd_path, SUDOERS_DEBUG_PLUGIN); |
1100 | | |
1101 | 23.7k | cmnd_in = ISSET(ctx->mode, MODE_CHECK) ? |
1102 | 20.2k | ctx->runas.argv[1] : ctx->runas.argv[0]; |
1103 | | |
1104 | 23.7k | free(ctx->user.cmnd_list); |
1105 | 23.7k | ctx->user.cmnd_list = NULL; |
1106 | 23.7k | free(ctx->user.cmnd); |
1107 | 23.7k | ctx->user.cmnd = NULL; |
1108 | 23.7k | canon_path_free(ctx->user.cmnd_dir); |
1109 | 23.7k | ctx->user.cmnd_dir = NULL; |
1110 | 23.7k | if (def_secure_path && !user_is_exempt(ctx)) |
1111 | 0 | path = def_secure_path; |
1112 | | |
1113 | 23.7k | ret = resolve_cmnd(ctx, cmnd_in, &cmnd_out, path, runchroot); |
1114 | 23.7k | if (ret == FOUND) { |
1115 | 15.7k | char *slash = strrchr(cmnd_out, '/'); |
1116 | 15.7k | if (slash != NULL) { |
1117 | 15.7k | *slash = '\0'; |
1118 | 15.7k | ctx->user.cmnd_dir = canon_path(cmnd_out); |
1119 | 15.7k | if (ctx->user.cmnd_dir == NULL && errno == ENOMEM) |
1120 | 0 | goto error; |
1121 | 15.7k | *slash = '/'; |
1122 | 15.7k | } |
1123 | 15.7k | } |
1124 | | |
1125 | 23.7k | if (ISSET(ctx->mode, MODE_CHECK)) |
1126 | 3.56k | ctx->user.cmnd_list = cmnd_out; |
1127 | 20.2k | else |
1128 | 20.2k | ctx->user.cmnd = cmnd_out; |
1129 | | |
1130 | 23.7k | debug_return_int(ret); |
1131 | 0 | error: |
1132 | 0 | free(cmnd_out); |
1133 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1134 | 0 | } |
1135 | | |
1136 | | /* |
1137 | | * Fill in ctx->user.cmnd, ctx->user.cmnd_stat and cmnd_status variables. |
1138 | | * Does not fill in ctx->user.cmnd_base. |
1139 | | */ |
1140 | | void |
1141 | | set_cmnd_status(struct sudoers_context *ctx, const char *runchroot) |
1142 | 6.39k | { |
1143 | 6.39k | cmnd_status = set_cmnd_path(ctx, runchroot); |
1144 | 6.39k | } |
1145 | | |
1146 | | /* |
1147 | | * Fill in ctx->user.cmnd, ctx->user.cmnd_args, ctx->user.cmnd_base and |
1148 | | * ctx->user.cmnd_stat variables and apply any command-specific defaults entries. |
1149 | | */ |
1150 | | static int |
1151 | | set_cmnd(struct sudoers_context *ctx) |
1152 | 29.2k | { |
1153 | 29.2k | struct sudo_nss *nss; |
1154 | 29.2k | int ret = FOUND; |
1155 | 29.2k | debug_decl(set_cmnd, SUDOERS_DEBUG_PLUGIN); |
1156 | | |
1157 | | /* Allocate ctx->user.cmnd_stat for find_path() and match functions. */ |
1158 | 29.2k | free(ctx->user.cmnd_stat); |
1159 | 29.2k | ctx->user.cmnd_stat = calloc(1, sizeof(struct stat)); |
1160 | 29.2k | if (ctx->user.cmnd_stat == NULL) { |
1161 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1162 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1163 | 0 | } |
1164 | | |
1165 | | /* Re-initialize for when we are called multiple times. */ |
1166 | 29.2k | free(ctx->runas.cmnd); |
1167 | 29.2k | ctx->runas.cmnd = NULL; |
1168 | | |
1169 | 29.2k | if (ISSET(ctx->mode, MODE_RUN|MODE_EDIT|MODE_CHECK)) { |
1170 | 18.5k | if (!ISSET(ctx->mode, MODE_EDIT)) { |
1171 | 17.3k | const char *runchroot = ctx->runas.chroot; |
1172 | 17.3k | if (runchroot == NULL && def_runchroot != NULL && |
1173 | 17.3k | strcmp(def_runchroot, "*") != 0) |
1174 | 17.3k | runchroot = def_runchroot; |
1175 | | |
1176 | 17.3k | ret = set_cmnd_path(ctx, runchroot); |
1177 | 17.3k | if (ret == NOT_FOUND_ERROR) { |
1178 | 0 | if (errno == ENAMETOOLONG) { |
1179 | 0 | audit_failure(ctx, ctx->runas.argv, N_("command too long")); |
1180 | 0 | } |
1181 | 0 | log_warning(ctx, 0, "%s", ctx->runas.argv[0]); |
1182 | 0 | debug_return_int(ret); |
1183 | 0 | } |
1184 | 17.3k | } |
1185 | | |
1186 | | /* set ctx->user.cmnd_args */ |
1187 | 18.5k | free(ctx->user.cmnd_args); |
1188 | 18.5k | ctx->user.cmnd_args = NULL; |
1189 | 18.5k | if (ISSET(ctx->mode, MODE_CHECK)) { |
1190 | 3.56k | if (ctx->runas.argc > 2) { |
1191 | | /* Skip the command being listed in ctx->runas.argv[1]. */ |
1192 | 176 | ctx->user.cmnd_args = strvec_join(ctx->runas.argv + 2, ' ', NULL); |
1193 | 176 | if (ctx->user.cmnd_args == NULL) |
1194 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1195 | 176 | } |
1196 | 14.9k | } else if (ctx->runas.argc > 1) { |
1197 | 1.41k | if (ISSET(ctx->mode, MODE_SHELL|MODE_LOGIN_SHELL) && |
1198 | 1.41k | ISSET(ctx->mode, MODE_RUN)) { |
1199 | | /* |
1200 | | * When running a command via a shell, the sudo front-end |
1201 | | * escapes potential meta chars. We unescape non-spaces |
1202 | | * for sudoers matching and logging purposes. |
1203 | | * TODO: move escaping to the policy plugin instead |
1204 | | */ |
1205 | 725 | ctx->user.cmnd_args = strvec_join(ctx->runas.argv + 1, ' ', |
1206 | 725 | strlcpy_unescape); |
1207 | 725 | } else { |
1208 | 690 | ctx->user.cmnd_args = strvec_join(ctx->runas.argv + 1, ' ', |
1209 | 690 | NULL); |
1210 | 690 | } |
1211 | 1.41k | if (ctx->user.cmnd_args == NULL) |
1212 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1213 | 1.41k | } |
1214 | 18.5k | } |
1215 | 29.2k | if (ctx->user.cmnd == NULL) { |
1216 | 15.6k | ctx->user.cmnd = strdup(ctx->runas.argv[0]); |
1217 | 15.6k | if (ctx->user.cmnd == NULL) |
1218 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1219 | 15.6k | } |
1220 | 29.2k | ctx->user.cmnd_base = sudo_basename(ctx->user.cmnd); |
1221 | | |
1222 | | /* Convert "sudo sudoedit" -> "sudoedit" */ |
1223 | 29.2k | if (ISSET(ctx->mode, MODE_RUN) && strcmp(ctx->user.cmnd_base, "sudoedit") == 0) { |
1224 | 288 | char *new_cmnd; |
1225 | | |
1226 | 288 | CLR(ctx->mode, MODE_RUN); |
1227 | 288 | SET(ctx->mode, MODE_EDIT); |
1228 | 288 | sudo_warnx("%s", U_("sudoedit doesn't need to be run via sudo")); |
1229 | 288 | if ((new_cmnd = strdup("sudoedit")) == NULL) { |
1230 | 0 | sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); |
1231 | 0 | debug_return_int(NOT_FOUND_ERROR); |
1232 | 0 | } |
1233 | 288 | free(ctx->user.cmnd); |
1234 | 288 | ctx->user.cmnd_base = ctx->user.cmnd = new_cmnd; |
1235 | 288 | } |
1236 | | |
1237 | 29.2k | TAILQ_FOREACH(nss, snl, entries) { |
1238 | | /* Missing/invalid defaults is not a fatal error. */ |
1239 | 29.2k | (void)update_defaults(ctx, nss->parse_tree, NULL, SETDEF_CMND, false); |
1240 | 29.2k | } |
1241 | | |
1242 | 29.2k | debug_return_int(ret); |
1243 | 29.2k | } |
1244 | | |
1245 | | static int |
1246 | | open_file(const char *path, int flags) |
1247 | 0 | { |
1248 | 0 | int fd; |
1249 | 0 | debug_decl(open_file, SUDOERS_DEBUG_PLUGIN); |
1250 | |
|
1251 | 0 | if (!set_perms(NULL, PERM_SUDOERS)) |
1252 | 0 | debug_return_int(-1); |
1253 | | |
1254 | 0 | fd = open(path, flags); |
1255 | 0 | if (fd == -1 && errno == EACCES && geteuid() != ROOT_UID) { |
1256 | | /* |
1257 | | * If we tried to open sudoers as non-root but got EACCES, |
1258 | | * try again as root. |
1259 | | */ |
1260 | 0 | int serrno = errno; |
1261 | 0 | if (restore_perms() && set_perms(NULL, PERM_ROOT)) |
1262 | 0 | fd = open(path, flags); |
1263 | 0 | errno = serrno; |
1264 | 0 | } |
1265 | 0 | if (!restore_perms()) { |
1266 | | /* unable to change back to root */ |
1267 | 0 | if (fd != -1) { |
1268 | 0 | close(fd); |
1269 | 0 | fd = -1; |
1270 | 0 | } |
1271 | 0 | } |
1272 | |
|
1273 | 0 | debug_return_int(fd); |
1274 | 0 | } |
1275 | | |
1276 | | /* |
1277 | | * Open sudoers file and check mode/owner/type. |
1278 | | * Returns a handle to the sudoers file or NULL on error. |
1279 | | */ |
1280 | | FILE * |
1281 | | open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen) |
1282 | 0 | { |
1283 | 0 | char fname[PATH_MAX]; |
1284 | 0 | FILE *fp = NULL; |
1285 | 0 | struct stat sb; |
1286 | 0 | int error, fd; |
1287 | 0 | debug_decl(open_sudoers, SUDOERS_DEBUG_PLUGIN); |
1288 | |
|
1289 | 0 | fd = sudo_open_conf_path(path, fname, sizeof(fname), open_file); |
1290 | 0 | if (sudoers_ctx.parser_conf.ignore_perms) { |
1291 | | /* Skip sudoers security checks when ignore_perms is set. */ |
1292 | 0 | if (fd == -1 || fstat(fd, &sb) == -1) |
1293 | 0 | error = SUDO_PATH_MISSING; |
1294 | 0 | else |
1295 | 0 | error = SUDO_PATH_SECURE; |
1296 | 0 | } else { |
1297 | 0 | error = sudo_secure_fd(fd, S_IFREG, sudoers_file_uid(), |
1298 | 0 | sudoers_file_gid(), &sb); |
1299 | 0 | } |
1300 | 0 | switch (error) { |
1301 | 0 | case SUDO_PATH_SECURE: |
1302 | | /* |
1303 | | * Make sure we can read the file so we can present the |
1304 | | * user with a reasonable error message (unlike the lexer). |
1305 | | */ |
1306 | 0 | if ((fp = fdopen(fd, "r")) == NULL) { |
1307 | 0 | log_warning(&sudoers_ctx, SLOG_PARSE_ERROR, |
1308 | 0 | N_("unable to open %s"), fname); |
1309 | 0 | } else { |
1310 | 0 | fd = -1; |
1311 | 0 | if (sb.st_size != 0 && fgetc(fp) == EOF) { |
1312 | 0 | log_warning(&sudoers_ctx, SLOG_PARSE_ERROR, |
1313 | 0 | N_("unable to read %s"), fname); |
1314 | 0 | fclose(fp); |
1315 | 0 | fp = NULL; |
1316 | 0 | } else { |
1317 | | /* Rewind fp and set close on exec flag. */ |
1318 | 0 | rewind(fp); |
1319 | 0 | (void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC); |
1320 | 0 | if (outfile != NULL) { |
1321 | 0 | *outfile = sudo_rcstr_dup(fname); |
1322 | 0 | if (*outfile == NULL) { |
1323 | 0 | sudo_warnx(U_("%s: %s"), __func__, |
1324 | 0 | U_("unable to allocate memory")); |
1325 | 0 | fclose(fp); |
1326 | 0 | fp = NULL; |
1327 | 0 | } |
1328 | 0 | } |
1329 | 0 | } |
1330 | 0 | } |
1331 | 0 | break; |
1332 | 0 | case SUDO_PATH_MISSING: |
1333 | 0 | log_warning(&sudoers_ctx, SLOG_PARSE_ERROR, |
1334 | 0 | N_("unable to open %s"), path); |
1335 | 0 | break; |
1336 | 0 | case SUDO_PATH_BAD_TYPE: |
1337 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1338 | 0 | N_("%s is not a regular file"), fname); |
1339 | 0 | break; |
1340 | 0 | case SUDO_PATH_WRONG_OWNER: |
1341 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1342 | 0 | N_("%s is owned by uid %u, should be %u"), fname, |
1343 | 0 | (unsigned int)sb.st_uid, (unsigned int)sudoers_file_uid()); |
1344 | 0 | break; |
1345 | 0 | case SUDO_PATH_WORLD_WRITABLE: |
1346 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1347 | 0 | N_("%s is world writable"), fname); |
1348 | 0 | break; |
1349 | 0 | case SUDO_PATH_GROUP_WRITABLE: |
1350 | 0 | log_warningx(&sudoers_ctx, SLOG_PARSE_ERROR, |
1351 | 0 | N_("%s is owned by gid %u, should be %u"), fname, |
1352 | 0 | (unsigned int)sb.st_gid, (unsigned int)sudoers_file_gid()); |
1353 | 0 | break; |
1354 | 0 | default: |
1355 | 0 | sudo_warnx("%s: internal error, unexpected error %d", __func__, error); |
1356 | 0 | break; |
1357 | 0 | } |
1358 | | |
1359 | 0 | if (fp == NULL && fd != -1) |
1360 | 0 | close(fd); |
1361 | |
|
1362 | 0 | debug_return_ptr(fp); |
1363 | 0 | } |
1364 | | |
1365 | | #ifdef HAVE_LOGIN_CAP_H |
1366 | | static bool |
1367 | | set_loginclass(struct sudoers_context *ctx) |
1368 | | { |
1369 | | const struct passwd *pw = ctx->runas.pw ? ctx->runas.pw : ctx->user.pw; |
1370 | | const unsigned int errflags = SLOG_RAW_MSG; |
1371 | | login_cap_t *lc; |
1372 | | bool ret = true; |
1373 | | debug_decl(set_loginclass, SUDOERS_DEBUG_PLUGIN); |
1374 | | |
1375 | | if (!def_use_loginclass) |
1376 | | goto done; |
1377 | | |
1378 | | if (ctx->runas.class && strcmp(ctx->runas.class, "-") != 0) { |
1379 | | if (ctx->user.uid != 0 && pw->pw_uid != 0) { |
1380 | | sudo_warnx(U_("only root can use \"-c %s\""), ctx->runas.class); |
1381 | | ret = false; |
1382 | | goto done; |
1383 | | } |
1384 | | } else { |
1385 | | ctx->runas.class = pw->pw_class; |
1386 | | if (!ctx->runas.class || !*ctx->runas.class) |
1387 | | ctx->runas.class = (char *) |
1388 | | ((pw->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS); |
1389 | | } |
1390 | | |
1391 | | /* Make sure specified login class is valid. */ |
1392 | | lc = login_getclass(ctx->runas.class); |
1393 | | if (!lc || !lc->lc_class || strcmp(lc->lc_class, ctx->runas.class) != 0) { |
1394 | | /* |
1395 | | * Don't make it an error if the user didn't specify the login |
1396 | | * class themselves. We do this because if login.conf gets |
1397 | | * corrupted we want the admin to be able to use sudo to fix it. |
1398 | | */ |
1399 | | log_warningx(ctx, errflags, N_("unknown login class %s"), |
1400 | | ctx->runas.class); |
1401 | | def_use_loginclass = false; |
1402 | | if (ctx->runas.class) |
1403 | | ret = false; |
1404 | | } |
1405 | | login_close(lc); |
1406 | | done: |
1407 | | debug_return_bool(ret); |
1408 | | } |
1409 | | #else |
1410 | | static bool |
1411 | | set_loginclass(struct sudoers_context *ctx) |
1412 | 21.5k | { |
1413 | 21.5k | return true; |
1414 | 21.5k | } |
1415 | | #endif /* HAVE_LOGIN_CAP_H */ |
1416 | | |
1417 | | /* |
1418 | | * Get passwd entry for the user we are going to run commands as |
1419 | | * and store it in ctx->runas.pw. By default, commands run as "root". |
1420 | | */ |
1421 | | static bool |
1422 | | set_runaspw(struct sudoers_context *ctx, const char *user, bool quiet) |
1423 | 48.4k | { |
1424 | 48.4k | struct passwd *pw = NULL; |
1425 | 48.4k | debug_decl(set_runaspw, SUDOERS_DEBUG_PLUGIN); |
1426 | | |
1427 | 48.4k | unknown_runas_uid = false; |
1428 | 48.4k | if (*user == '#') { |
1429 | 293 | const char *errstr; |
1430 | 293 | uid_t uid = sudo_strtoid(user + 1, &errstr); |
1431 | 293 | if (errstr == NULL) { |
1432 | 290 | if ((pw = sudo_getpwuid(uid)) == NULL) { |
1433 | 90 | unknown_runas_uid = true; |
1434 | 90 | pw = sudo_fakepwnam(user, ctx->user.gid); |
1435 | 90 | } |
1436 | 290 | } |
1437 | 293 | } |
1438 | 48.4k | if (pw == NULL) { |
1439 | 48.1k | if ((pw = sudo_getpwnam(user)) == NULL) { |
1440 | 45 | if (!quiet) |
1441 | 45 | log_warningx(ctx, SLOG_AUDIT, N_("unknown user %s"), user); |
1442 | 45 | debug_return_bool(false); |
1443 | 45 | } |
1444 | 48.1k | } |
1445 | 48.3k | if (ctx->runas.pw != NULL) |
1446 | 26.8k | sudo_pw_delref(ctx->runas.pw); |
1447 | 48.3k | ctx->runas.pw = pw; |
1448 | 48.3k | debug_return_bool(true); |
1449 | 48.3k | } |
1450 | | |
1451 | | /* |
1452 | | * Get group entry for the group we are going to run commands as |
1453 | | * and store it in ctx->runas.gr. |
1454 | | */ |
1455 | | static bool |
1456 | | set_runasgr(struct sudoers_context *ctx, const char *group, bool quiet) |
1457 | 711 | { |
1458 | 711 | struct group *gr = NULL; |
1459 | 711 | debug_decl(set_runasgr, SUDOERS_DEBUG_PLUGIN); |
1460 | | |
1461 | 711 | unknown_runas_gid = false; |
1462 | 711 | if (*group == '#') { |
1463 | 543 | const char *errstr; |
1464 | 543 | gid_t gid = sudo_strtoid(group + 1, &errstr); |
1465 | 543 | if (errstr == NULL) { |
1466 | 542 | if ((gr = sudo_getgrgid(gid)) == NULL) { |
1467 | 411 | unknown_runas_gid = true; |
1468 | 411 | gr = sudo_fakegrnam(group); |
1469 | 411 | } |
1470 | 542 | } |
1471 | 543 | } |
1472 | 711 | if (gr == NULL) { |
1473 | 169 | if ((gr = sudo_getgrnam(group)) == NULL) { |
1474 | 118 | if (!quiet) |
1475 | 118 | log_warningx(ctx, SLOG_AUDIT, N_("unknown group %s"), group); |
1476 | 118 | debug_return_bool(false); |
1477 | 118 | } |
1478 | 169 | } |
1479 | 593 | if (ctx->runas.gr != NULL) |
1480 | 0 | sudo_gr_delref(ctx->runas.gr); |
1481 | 593 | ctx->runas.gr = gr; |
1482 | 593 | debug_return_bool(true); |
1483 | 593 | } |
1484 | | |
1485 | | /* |
1486 | | * Callback for runas_default sudoers setting. |
1487 | | */ |
1488 | | bool |
1489 | | cb_runas_default(struct sudoers_context *ctx, const char *file, int line, |
1490 | | int column, const union sudo_defs_val *sd_un, int op) |
1491 | 27.9k | { |
1492 | 27.9k | debug_decl(cb_runas_default, SUDOERS_DEBUG_PLUGIN); |
1493 | | |
1494 | | /* Only reset runaspw if user didn't specify one. */ |
1495 | 27.9k | if (ctx->runas.user == NULL && ctx->runas.group == NULL) |
1496 | 26.8k | debug_return_bool(set_runaspw(ctx, sd_un->str, true)); |
1497 | 1.11k | debug_return_bool(true); |
1498 | 1.11k | } |
1499 | | |
1500 | | /* |
1501 | | * Cleanup hook for sudo_fatal()/sudo_fatalx() |
1502 | | * Also called at policy close time. |
1503 | | */ |
1504 | | void |
1505 | | sudoers_cleanup(void) |
1506 | 27.3k | { |
1507 | 27.3k | struct sudo_nss *nss; |
1508 | 27.3k | struct defaults *def; |
1509 | 27.3k | debug_decl(sudoers_cleanup, SUDOERS_DEBUG_PLUGIN); |
1510 | | |
1511 | 27.3k | if (snl != NULL) { |
1512 | 21.5k | TAILQ_FOREACH(nss, snl, entries) { |
1513 | 21.5k | nss->close(&sudoers_ctx, nss); |
1514 | 21.5k | } |
1515 | 21.5k | snl = NULL; |
1516 | 21.5k | reset_parser(); |
1517 | 21.5k | } |
1518 | 45.1k | while ((def = TAILQ_FIRST(&initial_defaults)) != NULL) { |
1519 | 17.8k | TAILQ_REMOVE(&initial_defaults, def, entries); |
1520 | 17.8k | free(def->var); |
1521 | 17.8k | free(def->val); |
1522 | 17.8k | free(def); |
1523 | 17.8k | } |
1524 | 27.3k | need_reinit = false; |
1525 | 27.3k | if (def_group_plugin) |
1526 | 0 | group_plugin_unload(); |
1527 | 27.3k | sudoers_ctx_free(&sudoers_ctx); |
1528 | 27.3k | sudo_freepwcache(); |
1529 | 27.3k | sudo_freegrcache(); |
1530 | 27.3k | canon_path_free_cache(); |
1531 | | |
1532 | | /* We must free the cached environment before running g/c. */ |
1533 | 27.3k | env_free(); |
1534 | | |
1535 | | /* Run garbage collector. */ |
1536 | 27.3k | sudoers_gc_run(); |
1537 | | |
1538 | | /* Clear globals */ |
1539 | 27.3k | prev_user = NULL; |
1540 | | |
1541 | 27.3k | debug_return; |
1542 | 27.3k | } |
1543 | | |
1544 | | bool |
1545 | | sudoers_set_mode(unsigned int flags, unsigned int mask) |
1546 | 36.6k | { |
1547 | 36.6k | SET(sudoers_ctx.mode, flags); |
1548 | 36.6k | return ((sudoers_ctx.mode & mask) == sudoers_ctx.mode); |
1549 | 36.6k | } |
1550 | | |
1551 | | const struct sudoers_context * |
1552 | | sudoers_get_context(void) |
1553 | 72.5k | { |
1554 | 72.5k | return &sudoers_ctx; |
1555 | 72.5k | } |
1556 | | |
1557 | | bool |
1558 | | sudoers_set_log_format(enum def_tuple tuple) |
1559 | 0 | { |
1560 | 0 | enum eventlog_format format; |
1561 | 0 | debug_decl(cb_log_format, SUDOERS_DEBUG_PLUGIN); |
1562 | | |
1563 | | /* FFR - make "json" an alias for EVLOG_JSON_COMPACT instead. */ |
1564 | 0 | switch (tuple) { |
1565 | 0 | case json_compact: |
1566 | 0 | format = EVLOG_JSON_COMPACT; |
1567 | 0 | break; |
1568 | 0 | case json: |
1569 | 0 | case json_pretty: |
1570 | 0 | format = EVLOG_JSON_PRETTY; |
1571 | 0 | break; |
1572 | 0 | case sudo: |
1573 | 0 | format = EVLOG_SUDO; |
1574 | 0 | break; |
1575 | 0 | default: |
1576 | 0 | debug_return_bool(false); |
1577 | 0 | } |
1578 | 0 | eventlog_set_format(format); |
1579 | |
|
1580 | 0 | debug_return_bool(true); |
1581 | 0 | } |